ValidationBadge | CoUI
LogoCoUI

ValidationBadge

입력값의 유효성 검증 결과를 아이콘과 텍스트로 표시하는 배지 컴포넌트

ValidationBadge#

폼 필드의 유효성 검증 결과를 아이콘과 텍스트로 시각적으로 표시하는 배지 컴포넌트입니다. 비밀번호 강도, 이메일 형식 등 검증 상태 안내에 적합합니다.

Live Preview#

사용 시기 (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#

속성타입기본값설명
labelString필수표시할 검증 조건 텍스트
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)radiusicon
Extra Small CoreValidationBadgeSize.extraSmall space2 / space4 radius4 space12
Small (default) CoreValidationBadgeSize.small space2 / space6 radius4 space12
Medium CoreValidationBadgeSize.medium space4 / space8 radius8 space14

동작 스펙 (Behavior)#

인터랙션#

  • 표시 전용. 인터랙션 없음.
  • state prop 변경 시 색상/아이콘 즉시 업데이트.

상태 전환#

  • pendingvalid: 회색 배경 + dot 아이콘 → 옅은 success tint + check.
  • validinvalid: 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)#

항목FlutterWeb
클래스명ValidationBadgeValidationBadge
색상 colorScheme.success/error + scaleAlpha(0.1/0.2/0.6) bg-${cs.success}/10 등 Tailwind alpha (Card 패턴)
아이콘 Icon(LucideIcons.*) Icon(LucideIcons.*) (동일 토큰)
  • 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,
    ),
  ],
)