Validated#
폼 필드를 감싸 유효성 state 를 필드 자신에게 전파하고 아래 message 를 표시하는 프레젠테이션 래퍼입니다.
invalid 상태는 감싼 필드(예: TextField)의 자체 error variant 를 작동시켜 필드의 보더와 포커스 링이 에러 톤으로 바뀝니다 — 별도의 겹 링을 그리지 않습니다.
pending 상태에서는 메시지가 숨겨지고, invalid / valid 메시지는 ValidatorHint
로 렌더됩니다. Flutter / Web 1:1 동일 API 입니다.
Live Preview#
필수 항목입니다.
class ValidatedDefaultExample extends StatelessComponent {
const ValidatedDefaultExample({super.key});
@override
Component build(BuildContext context) {
return div(
styles: Styles(raw: {'width': '320px'}),
[
Validated(
state: CoreValidationState.invalid,
message: Text('필수 항목입니다.'),
child: TextField(placeholder: Text('이름')),
),
],
);
}
}
class ValidatedDefaultExample extends StatelessWidget {
const ValidatedDefaultExample({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
width: 320,
child: Validated(
state: CoreValidationState.invalid,
message: const Text('필수 항목입니다.'),
child: TextField(placeholder: const Text('이름')),
),
);
}
}
필수 항목입니다.
class ValidatedChainExample extends StatelessComponent {
const ValidatedChainExample({super.key});
@override
Component build(BuildContext context) {
return div(
styles: Styles(raw: {'width': '320px'}),
[
Validated(
state: CoreValidationState.invalid,
message: Text('필수 항목입니다.'),
child: TextField(placeholder: Text('이름')),
).withStyle(
const CoreValidatedStyle(
fieldMessageGapStyle: CoreGapStyle(size: CoreSpace.space16),
validatorHintStyle: CoreValidatorHintStyle(
accentColor: CoreColor.token(CoreColors.warning),
iconStyle: CoreIconStyle(size: CoreIconSize.size16),
iconLabelGapStyle: CoreGapStyle(size: CoreSpace.space8),
),
),
),
],
);
}
}
class ValidatedChainExample extends StatelessWidget {
const ValidatedChainExample({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
width: 320,
child:
Validated(
state: CoreValidationState.invalid,
message: const Text('필수 항목입니다.'),
child: TextField(placeholder: const Text('이름')),
).withStyle(
const CoreValidatedStyle(
fieldMessageGapStyle: CoreGapStyle(size: CoreSpace.space16),
validatorHintStyle: CoreValidatorHintStyle(
accentColor: CoreColor.token(CoreColors.warning),
iconStyle: CoreIconStyle(size: CoreIconSize.size16),
iconLabelGapStyle: CoreGapStyle(size: CoreSpace.space8),
),
),
),
);
}
}
사용법#
Validated(
state: CoreValidationState.invalid,
message: const Text('필수 항목입니다.'),
child: TextField(placeholder: const Text('이름')),
)
빠른 오버라이드 (Chain)#
이미 만든 Validated 인스턴스에 스타일을 빠르게 덧붙이고 싶다면 withStyle 체인을 쓸 수 있습니다. 생성자의 style 슬롯 인자와 동일하게 동작하지만, 이미 구성된 위젯 위에서 바로 이어 쓸 수 있습니다.
class ValidatedChainExample extends StatelessWidget {
const ValidatedChainExample({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
width: 320,
child:
Validated(
state: CoreValidationState.invalid,
message: const Text('필수 항목입니다.'),
child: TextField(placeholder: const Text('이름')),
).withStyle(
const CoreValidatedStyle(
fieldMessageGapStyle: CoreGapStyle(size: CoreSpace.space16),
validatorHintStyle: CoreValidatorHintStyle(
accentColor: CoreColor.token(CoreColors.warning),
iconStyle: CoreIconStyle(size: CoreIconSize.size16),
iconLabelGapStyle: CoreGapStyle(size: CoreSpace.space8),
),
),
),
);
}
}
class ValidatedChainExample extends StatelessComponent {
const ValidatedChainExample({super.key});
@override
Component build(BuildContext context) {
return div(
styles: Styles(raw: {'width': '320px'}),
[
Validated(
state: CoreValidationState.invalid,
message: Text('필수 항목입니다.'),
child: TextField(placeholder: Text('이름')),
).withStyle(
const CoreValidatedStyle(
fieldMessageGapStyle: CoreGapStyle(size: CoreSpace.space16),
validatorHintStyle: CoreValidatorHintStyle(
accentColor: CoreColor.token(CoreColors.warning),
iconStyle: CoreIconStyle(size: CoreIconSize.size16),
iconLabelGapStyle: CoreGapStyle(size: CoreSpace.space8),
),
),
),
],
);
}
}
Props#
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
state |
CoreValidationState |
pending |
유효성 상태 (pending / valid / invalid) |
message |
Widget / Component? |
null |
필드 아래 메시지 (pending 이면 숨김) |
child |
Widget / Component? |
null |
감싸는 폼 필드 |
validatedStyle |
CoreValidatedStyle? |
null |
chrome / dimensional 단일 진입점 |
스타일 시스템 (Style System)#
CoreValidatedStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
validatorHintStyle |
CoreValidatorHintStyle? |
Override ring colour applied around the wrapped child. Nested [CoreValidatorHintStyle] slot for the composed message
ValidatorHint
. Raw-forwarded on top of [defaultValidatorHintStyle].
|
fieldMessageGapStyle |
CoreGapStyle? |
Gap style between the wrapped child and the message — nested [CoreGapStyle] slot forwarded to the
Gap
widget that separates the field from the message.
null
defers to [defaultFieldMessageGapStyle].
|
사용 가이드라인 (Usage Guidelines)#
✅ Do#
필드 자체의 에러 표현은 state 전파에 맡기기
Validated(
state: isSubmitting
? CoreValidationState.pending
: (hasError ? CoreValidationState.invalid : CoreValidationState.valid),
message: Text(errorText ?? '입력값이 확인되었습니다.'),
child: TextField(placeholder: const Text('이름')),
)
invalid 상태는 Data.inherit 로 감싼 TextField 에 전파되어 필드가 스스로 error variant(보더 + 포커스 링)를 적용합니다 — Validated 는 별도 링을 그리지 않습니다.
❌ Don't#
필드에 에러 색을 직접 또 지정하지 않기
// ❌ state 가 이미 전파하는 에러 표현을 필드에도 중복 지정
Validated(
state: CoreValidationState.invalid,
child: TextField(
textFieldStyle: CoreTextFieldStyle(
borderColor: CoreColor.token(CoreColors.error),
),
),
)
state 가 이미 필드의 error variant 를 켜므로, 여기에 색을 또 지정하면 두 값 중 어느 쪽이 이기는지 불명확해지고 같은 로직을 두 곳에서 유지하게 됩니다.
접근성 (Accessibility)#
역할 / Semantics#
어느 플랫폼도 역할을 내보내지 않습니다. Flutter 의 build 는 순수 Column 을 반환하고 Semantics 를 한 번도 호출하지 않으며, Web 은
role / aria-invalid / aria-describedby / aria-live
중 무엇도 붙지 않은 평범한 <div> 입니다.
키보드#
처리하는 키가 없습니다 — 자식과 Gap, ValidatorHint 를 합성하기만 합니다.
포커스#
포커스를 관리하지 않습니다. 유효하지 않은 필드로 포커스를 옮기지도, 트랩하거나 복원하지도 않습니다.
스크린 리더#
Validated 자체는 아무것도 알리지 않습니다. state 는 감싼 필드로 전파되지만(Flutter Data.inherit, Web
ValidationStateScope) 양쪽 모두 hasError 색 계산에만 쓰입니다 — 필드에는 어떤 유효성 플래그도 붙지 않습니다. 메시지는
ValidatorHint 로 렌더되고 심각도 아이콘은 양 플랫폼 모두 접근성 트리에서 제외되므로(ExcludeSemantics / aria-hidden="true"), 리더가 그 지점을 지나갈 때 메시지 문장은 일반 텍스트로 읽힙니다. 다만
메시지가 나타나는 순간에는 아무것도 안내되지 않습니다.
알려진 제약#
-
감싼 필드에
aria-invalid(Web) 나 대응하는Semantics플래그(Flutter) 가 설정되지 않습니다 — 유효성은 색 변화와 형제 메시지로만 표현됩니다. -
메시지가
aria-describedby로 필드와 연결되지 않고 live region 도 아니라서, 제출 후 뒤늦게 나타나는 에러는 안내되지 않습니다. - 실패한 필드로 포커스를 옮기지 않습니다.
state: pending은 메시지를 조용히 숨깁니다.
폼 제출 검증에 쓴다면 필드의 invalid 상태 노출, 메시지의 live region 처리, 실패 시 포커스 이동은 소비자가 직접 붙여야 합니다.
상태를 색으로 구분하는 만큼 강제 색상 모드가 특히 중요합니다 — 고대비 · 강제 색상처럼 전 컴포넌트에 공통으로 걸리는 축은 전역 접근성 축에서 다룹니다.