ValidationBadge#
폼 필드의 유효성 검증 결과를 아이콘과 텍스트로 시각적으로 표시하는 배지 컴포넌트입니다. 비밀번호 강도, 이메일 형식 등 검증 상태 안내에 적합합니다.
Live Preview#
class ValidationBadgeDefaultExample extends StatelessComponent {
const ValidationBadgeDefaultExample({super.key});
@override
Component build(BuildContext context) {
return const ValidationBadge(
label: 'At least 8 characters',
state: CoreValidationState.valid,
);
}
}
class ValidationBadgeDefaultExample extends StatelessWidget {
const ValidationBadgeDefaultExample({super.key});
@override
Widget build(BuildContext context) {
return const ValidationBadge(
label: 'At least 8 characters',
state: CoreValidationState.valid,
);
}
}
class ValidationBadgePendingExample extends StatelessComponent {
const ValidationBadgePendingExample({super.key});
@override
Component build(BuildContext context) {
return const ValidationBadge(
label: 'Awaiting input',
state: CoreValidationState.pending,
);
}
}
class ValidationBadgePendingExample extends StatelessWidget {
const ValidationBadgePendingExample({super.key});
@override
Widget build(BuildContext context) {
return const ValidationBadge(
label: 'Awaiting input',
state: CoreValidationState.pending,
);
}
}
class ValidationBadgeInvalidExample extends StatelessComponent {
const ValidationBadgeInvalidExample({super.key});
@override
Component build(BuildContext context) {
return const ValidationBadge(
label: 'Must contain a digit',
state: CoreValidationState.invalid,
);
}
}
class ValidationBadgeInvalidExample extends StatelessWidget {
const ValidationBadgeInvalidExample({super.key});
@override
Widget build(BuildContext context) {
return const ValidationBadge(
label: 'Must contain a digit',
state: CoreValidationState.invalid,
);
}
}
class ValidationBadgeMediumExample extends StatelessComponent {
const ValidationBadgeMediumExample({super.key});
@override
Component build(BuildContext context) {
return const ValidationBadge(
label: 'At least one uppercase letter',
state: CoreValidationState.valid,
size: CoreValidationBadgeSize.medium,
);
}
}
class ValidationBadgeMediumExample extends StatelessWidget {
const ValidationBadgeMediumExample({super.key});
@override
Widget build(BuildContext context) {
return const ValidationBadge(
label: 'At least one uppercase letter',
state: CoreValidationState.valid,
size: CoreValidationBadgeSize.medium,
);
}
}
class ValidationBadgeChainExample extends StatelessComponent {
const ValidationBadgeChainExample({super.key});
@override
Component build(BuildContext context) {
return const ValidationBadge(
label: 'At least 8 characters',
state: CoreValidationState.valid,
).withStyle(
const CoreValidationBadgeStyle(
validBackgroundColor: CoreColor.token(
CoreColors.primary,
opacity: CoreOpacity.opacity10,
),
validBorderColor: CoreColor.token(CoreColors.primary),
validLabelTextStyle: CoreTextStyle.token(
CoreTextStyles.labelMedium,
color: CoreColor.token(CoreColors.primary),
),
borderWidth: CoreStrokeWidth.stroke2,
iconStyle: CoreIconStyle(
size: CoreIconSize.size16,
color: CoreColor.token(CoreColors.primary),
),
iconLabelGapStyle: CoreGapStyle(size: CoreSpace.space8),
),
);
}
}
class ValidationBadgeChainExample extends StatelessWidget {
const ValidationBadgeChainExample({super.key});
@override
Widget build(BuildContext context) {
return const ValidationBadge(
label: 'At least 8 characters',
state: CoreValidationState.valid,
).withStyle(
const CoreValidationBadgeStyle(
validBackgroundColor: CoreColor.token(
CoreColors.primary,
opacity: CoreOpacity.opacity10,
),
validBorderColor: CoreColor.token(CoreColors.primary),
validLabelTextStyle: CoreTextStyle.token(
CoreTextStyles.labelMedium,
color: CoreColor.token(CoreColors.primary),
),
borderWidth: CoreStrokeWidth.stroke2,
iconStyle: CoreIconStyle(
size: CoreIconSize.size16,
color: CoreColor.token(CoreColors.primary),
),
iconLabelGapStyle: CoreGapStyle(size: CoreSpace.space8),
),
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 비밀번호 설정 시 각 조건(길이, 대문자, 숫자 포함 등)의 충족 여부를 실시간으로 표시할 때
- 아이디, 이메일 중복 확인 결과를 시각적으로 안내할 때
- 폼 필드 아래에 여러 검증 규칙을 체크리스트 형태로 나열할 때
- 입력 진행 중 실시간 유효성 상태를 사용자에게 보여줄 때
대신 다른 컴포넌트를 사용하세요:
Status: 시스템이나 사용자 상태(온라인/오프라인)를 표시할 때Banner또는Toast: 전체 폼 제출 후 오류 메시지를 표시할 때Text: 단순한 에러 메시지 텍스트만 필요할 때
기본 사용법 (Basic Usage)#
// 유효한 상태 (기본)
ValidationBadge(
label: '8자 이상',
state: CoreValidationState.valid,
)
// 무효한 상태
ValidationBadge(
label: '숫자 포함',
state: CoreValidationState.invalid,
)
// 입력 전(pending) 상태
ValidationBadge(
label: '대문자 포함',
state: CoreValidationState.pending,
)
// bool로 간편 생성 (true → valid, false → invalid)
ValidationBadge.fromBool(
label: '대문자 포함',
isValid: hasUpperCase,
)
// hasInput → !hasInput 이면 pending, 그 외엔 isValid 기준
ValidationBadge.withPending(
label: '숫자 포함',
hasInput: password.isNotEmpty,
isValid: hasNumber,
)
// 유효한 상태 (기본)
ValidationBadge(
label: '8자 이상',
state: CoreValidationState.valid,
)
// 무효한 상태
ValidationBadge(
label: '숫자 포함',
state: CoreValidationState.invalid,
)
// 입력 전(pending) 상태
ValidationBadge(
label: '대문자 포함',
state: CoreValidationState.pending,
)
// bool로 간편 생성
ValidationBadge.fromBool(
label: '대문자 포함',
isValid: hasUpperCase,
)
// hasInput 기반 3-state factory
ValidationBadge.withPending(
label: '숫자 포함',
hasInput: password.isNotEmpty,
isValid: hasNumber,
)
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
label | String | 필수 | 표시할 검증 조건 텍스트 |
state |
CoreValidationState |
필수 | 검증 상태 (pending / valid / invalid) |
pendingIcon |
IconName? |
LucideIcons.dot |
pending 상태에서 표시할 아이콘 override |
validIcon |
IconName? |
LucideIcons.check |
valid 상태에서 표시할 아이콘 override |
invalidIcon |
IconName? |
LucideIcons.x |
invalid 상태에서 표시할 아이콘 override |
size |
CoreValidationBadgeSize? |
small (token default) |
크기 토큰 (extraSmall / small / medium) |
style |
CoreValidationBadgeStyle? |
null |
상태별 색상 override (background/foreground/border × 3 state = 9 필드, CoreColor) |
스타일 시스템 (Style System)#
CoreValidationBadgeStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
pendingBackgroundColor |
CoreColor? |
Pending background color override. |
pendingLabelTextStyle |
CoreTextStyle? |
Pending label text style override. Text colour is carried via [CoreTextStyle.color] inside this slot (sb8 — raw
pendingForegroundColor
field removed).
null
defers to [defaultPendingLabelTextStyle] over the per-size role from [defaultsBySize]
[size].labelStyle
.
|
pendingBorderColor |
CoreColor? |
Pending border color override. |
validBackgroundColor |
CoreColor? |
Valid background color override. |
validLabelTextStyle |
CoreTextStyle? |
Valid label text style override. Text colour is carried via [CoreTextStyle.color] inside this slot (sb8 — raw
validForegroundColor
field removed).
null
defers to [defaultValidLabelTextStyle] over the per-size role from [defaultsBySize]
[size].labelStyle
.
|
validBorderColor |
CoreColor? |
Valid border color override. |
invalidBackgroundColor |
CoreColor? |
Invalid background color override. |
invalidLabelTextStyle |
CoreTextStyle? |
Invalid label text style override. Text colour is carried via [CoreTextStyle.color] inside this slot (sb8 — raw
invalidForegroundColor
field removed).
null
defers to [defaultInvalidLabelTextStyle] over the per-size role from [defaultsBySize]
[size].labelStyle
.
|
invalidBorderColor |
CoreColor? |
Invalid border color override. |
iconLabelGapStyle |
CoreGapStyle? |
Nested [CoreGapStyle] slot for the icon ↔ label gap. Forwarded straight to
Gap(gapStyle: …)
(Flutter) / reflected as the inline
gap
rem rule (Web).
null
defers to [CoreValidationBadgeStyle.defaultsBySize]
[size].iconLabelGapStyle
.
|
iconStyle |
CoreIconStyle? |
Nested [CoreIconStyle] slot for the state icon — forwarded to
Icon(iconStyle: …)
.
null
defers to the resolver-built base whose
size
comes from [CoreValidationBadgeStyle.defaultsBySize]
[size].iconSize
and whose
color
is the resolved per-state foreground (sb #2305 — flat
iconSize
+
iconColor
folded into one nested slot, mirroring the Web resolver's
iconStyle
). No paired
default*
: every value it would carry varies on an axis a
static const
cannot hold —
size
per size preset, and
color
per state on Flutter but deliberately absent on Web (the icon inherits the chip's colour). The slot is an overlay on the base each resolver composes, not a value with a baseline of its own.
|
borderWidth |
double? |
Border stroke width override (logical px). Overrides [defaultBorderWidth] when set. |
빠른 오버라이드 (Chain)#
이미 만든 ValidationBadge 인스턴스에 스타일을 빠르게 덧붙이고 싶다면 withStyle 체인을 쓸 수 있습니다. 생성자의 style 슬롯 인자와 동일하게 동작하지만, 이미 구성된 위젯 위에서 바로 이어 쓸 수 있습니다.
class ValidationBadgeChainExample extends StatelessWidget {
const ValidationBadgeChainExample({super.key});
@override
Widget build(BuildContext context) {
return const ValidationBadge(
label: 'At least 8 characters',
state: CoreValidationState.valid,
).withStyle(
const CoreValidationBadgeStyle(
validBackgroundColor: CoreColor.token(
CoreColors.primary,
opacity: CoreOpacity.opacity10,
),
validBorderColor: CoreColor.token(CoreColors.primary),
validLabelTextStyle: CoreTextStyle.token(
CoreTextStyles.labelMedium,
color: CoreColor.token(CoreColors.primary),
),
borderWidth: CoreStrokeWidth.stroke2,
iconStyle: CoreIconStyle(
size: CoreIconSize.size16,
color: CoreColor.token(CoreColors.primary),
),
iconLabelGapStyle: CoreGapStyle(size: CoreSpace.space8),
),
);
}
}
class ValidationBadgeChainExample extends StatelessComponent {
const ValidationBadgeChainExample({super.key});
@override
Component build(BuildContext context) {
return const ValidationBadge(
label: 'At least 8 characters',
state: CoreValidationState.valid,
).withStyle(
const CoreValidationBadgeStyle(
validBackgroundColor: CoreColor.token(
CoreColors.primary,
opacity: CoreOpacity.opacity10,
),
validBorderColor: CoreColor.token(CoreColors.primary),
validLabelTextStyle: CoreTextStyle.token(
CoreTextStyles.labelMedium,
color: CoreColor.token(CoreColors.primary),
),
borderWidth: CoreStrokeWidth.stroke2,
iconStyle: CoreIconStyle(
size: CoreIconSize.size16,
color: CoreColor.token(CoreColors.primary),
),
iconLabelGapStyle: CoreGapStyle(size: CoreSpace.space8),
),
);
}
}
변형 (Variants)#
Default (valid)#
가장 흔한 success 상태. soft success tint + check 아이콘.
ValidationBadge(
label: '8자 이상',
state: CoreValidationState.valid,
)
Pending#
입력 전 중립 상태. surface 배경 + 옅은 onSurface 텍스트 + dot 아이콘.
ValidationBadge(
label: '대문자 포함',
state: CoreValidationState.pending,
)
Invalid#
실패 상태. soft error tint + X 아이콘.
ValidationBadge(
label: '숫자 포함',
state: CoreValidationState.invalid,
)
Medium size#
typography 가 한 단계 크고 padding 도 더 넉넉한 변형.
ValidationBadge(
label: '대문자 포함',
state: CoreValidationState.valid,
size: CoreValidationBadgeSize.medium,
)
크기 (Sizes)#
| 크기 | 토큰 | padding (v/h) | radius | icon |
|---|---|---|---|---|
| Extra Small | CoreValidationBadgeSize.extraSmall |
space2 / space4 |
radius4 |
space12 |
| Small (default) | CoreValidationBadgeSize.small |
space2 / space6 |
radius4 |
space12 |
| Medium | CoreValidationBadgeSize.medium |
space4 / space8 |
radius8 |
space14 |
동작 스펙 (Behavior)#
인터랙션#
- 표시 전용. 인터랙션 없음.
stateprop 변경 시 색상/아이콘 즉시 업데이트.
상태 전환#
pending→valid: 회색 배경 + dot 아이콘 → 옅은 success tint + check.valid→invalid: success tint → error tint + X.
사용 가이드라인 (Usage Guidelines)#
✅ Do#
실시간 검증 + withPending로 미입력 단계는 중립 표시
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ValidationBadge.withPending(
label: '8자 이상',
hasInput: password.isNotEmpty,
isValid: password.length >= 8,
),
ValidationBadge.withPending(
label: '대문자 포함',
hasInput: password.isNotEmpty,
isValid: hasUpperCase,
),
],
)
입력 시작 전엔 pending(중립), 시작 후엔 valid/invalid 로 전환됩니다.
❌ Don't#
입력 전부터 모든 조건을 invalid 로 표시 금지
// ❌ 입력 전부터 모든 조건이 빨간 X
ValidationBadge(label: '8자 이상', state: CoreValidationState.invalid)
ValidationBadge(label: '대문자', state: CoreValidationState.invalid)
아직 입력 안 된 상태에서 모두 실패로 표시되면 위압감을 줍니다. pending 상태 또는 withPending factory 를 사용하세요.
✅ Do#
긍정적 메시지로 조건 표현
ValidationBadge.fromBool(label: '영문 대문자 포함', isValid: hasUpperCase)
ValidationBadge.fromBool(label: '숫자 1개 이상', isValid: hasNumber)
"대문자 미포함"보다 "영문 대문자 포함"처럼 충족해야 할 조건을 긍정형으로 표현하면 더 명확합니다.
❌ Don't#
너무 많은 조건을 한 번에 표시 금지
5~6개 이내로 핵심 조건만. 그 이상은 사용자에게 부담.
접근성 (Accessibility)#
- Flutter:
Semantics(label: '$label: ${state.name}')적용 권장. -
Web: 컨테이너
role="status"자동 적용 (필요 시aria-live="polite"추가).
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | ValidationBadge | ValidationBadge |
| 색상 | colorScheme.success/error + scaleAlpha(0.1/0.2/0.6) |
bg-${cs.success}/10 등 Tailwind alpha (Card 패턴) |
| 아이콘 | Icon(LucideIcons.*) |
Icon(LucideIcons.*) (동일 토큰) |
관련 컴포넌트 (Related Components)#
- TextField: ValidationBadge 와 함께 폼 필드 검증 UI 구성.
- Form: 전체 폼 수준의 검증 관리.
- Status: 시스템/사용자 상태 표시 (ValidationBadge 와 구분).
조합 예제#
// ValidationBadge + Input 조합으로 비밀번호 설정 UI
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Input(label: '새 비밀번호', obscureText: true, onChanged: handlePasswordChange),
Gap(size: CoreSpace.space12),
ValidationBadge.withPending(
label: '8자 이상',
hasInput: password.isNotEmpty,
isValid: password.length >= 8,
),
ValidationBadge.withPending(
label: '영문 대문자 1자 이상',
hasInput: password.isNotEmpty,
isValid: hasUpperCase,
),
ValidationBadge.withPending(
label: '숫자 1자 이상',
hasInput: password.isNotEmpty,
isValid: hasNumber,
),
],
)