TextField | CoUI

TextField

텍스트 입력 필드 컴포넌트

TextField#

사용자로부터 텍스트를 입력받는 컴포넌트입니다. 다양한 타입과 상태를 지원합니다.

Live Preview#

Web
Flutter
Loading Flutter...
class TextFieldDefaultExample extends StatefulComponent {
  const TextFieldDefaultExample({super.key});

  @override
  State<TextFieldDefaultExample> createState() =>
      _TextFieldDefaultExampleState();
}

class _TextFieldDefaultExampleState extends State<TextFieldDefaultExample> {
  @override
  Component build(BuildContext context) {
    return CoTextField(
      placeholder: text('Enter text...'),
    );
  }
}
class TextFieldDefaultExample extends StatefulWidget {
  const TextFieldDefaultExample({super.key});

  @override
  State<TextFieldDefaultExample> createState() =>
      _TextFieldDefaultExampleState();
}

class _TextFieldDefaultExampleState extends State<TextFieldDefaultExample> {
  @override
  Widget build(BuildContext context) {
    return CoTextField(
      placeholder: const Text('Enter text...'),
    );
  }
}

사용 시기 (When to Use)#

이 컴포넌트를 사용하세요:

  • 사용자로부터 짧은 텍스트를 입력받을 때 (이름, 이메일, 비밀번호 등)
  • 검색 필드가 필요할 때
  • 숫자, 전화번호 등 특정 형식의 입력이 필요할 때

대신 다른 컴포넌트를 사용하세요:

  • TextArea: 여러 줄의 긴 텍스트를 입력받을 때
  • Select: 미리 정해진 옵션 중 선택할 때
  • Autocomplete: 입력하면서 추천 목록에서 선택할 때
  • DatePicker: 날짜를 입력받을 때

기본 사용법 (Basic Usage)#

// 기본 입력
CoTextField(
  onChanged: handleNameChange,
  placeholder: Text('이름을 입력하세요'),
)

// 비밀번호 입력
CoTextField(
  obscureText: true,
  placeholder: Text('비밀번호'),
)

// 라벨 및 에러
CoTextField(
  label: '이메일',
  onChanged: handleEmailChange,
  errorText: '올바른 이메일을 입력하세요',
)
// 기본 입력
CoTextField(
  onChanged: handleNameChange,
  placeholder: text('이름을 입력하세요'),
)

// 비밀번호 입력
CoTextField(
  obscureText: true,
  placeholder: text('비밀번호'),
)

// 라벨 + 에러
CoTextField(
  label: '이메일',
  onChanged: handleEmailChange,
  errorText: '올바른 이메일을 입력하세요',
)

스타일 시스템 — textFieldStyle (Epic #1302)#

CoTextField 의 chrome / dimensional / cursor / 슬롯 텍스트 미세 조정은 단일 textFieldStyle 필드(CoreTextFieldStyle) 하나로 흐릅니다. CoTextArea 도 같은 CoreTextFieldStyle 을 공유 — chrome 99% 동일.

CoTextField(
  variant: CoreTextFieldVariant.outline,                  // ← 시맨틱 enum 위젯 파라미터
  size: CoreComponentSize.md,                              // ← 시맨틱 enum 위젯 파라미터
  onChanged: handleEmailChange,
  textFieldStyle: CoreTextFieldStyle(                      // ← chrome / 슬롯
    borderRadius: 8,
    paddingH: 16,
    paddingV: 12,
    focusBorderColor: cs.primary,
    cursorColor: cs.primary,
    valueStyle: CoreTextStyle(fontSize: 14),               // ← 입력 텍스트
    placeholderStyle: CoreTextStyle(color: cs.onSurfaceVariant),
    leadingIconStyle: CoreIconStyle(size: 18),
  ),
  leading: Icon(Icons.email),
  label: '이메일',
)

CoreTextFieldStyle 의 22 필드:

  • chrome: backgroundColor, borderColor, focusBorderColor, errorBorderColor, borderWidth, borderRadius
  • dimensional: height, minHeight, maxHeight, paddingH, paddingV, gap
  • cursor: cursorColor, cursorWidth, cursorRadius
  • nested slot: labelStyle, placeholderStyle, valueStyle, descriptionStyle, errorStyle, leadingIconStyle, trailingIconStyle

Resolve chain#

design system default for variant
  → CoreTextFieldTheme.style                       // 프로젝트 공통
  → CoreTextFieldTheme.variantStyles[widget.variant]   // variant 별
  → 부모 컴포넌트 슬롯 오버라이드
  → widget.textFieldStyle                          // 인스턴스별

nested slot styles 는 재귀 머지 — valueStyle.fontWeight 만 override 해도 valueStyle.fontSize / valueStyle.color 는 이전 레이어 값 유지.

Props / Parameters#

속성타입기본값설명
placeholder Widget? / Component? null 플레이스홀더
controller TextEditingController? null 텍스트 컨트롤러 (Flutter)
onChanged ValueChanged<String>? null 값 변경 콜백
labelString?null라벨 텍스트
description String? null 설명 텍스트
errorText String? null 에러 메시지 (설정 시 자동 error 스타일)
enabledbooltrue활성화 여부
obscureText bool false 비밀번호 마스킹
maxLinesint1최대 줄 수
variant CoreTextFieldVariant defaultVariant 시맨틱 변형 (위젯 파라미터)
size CoreComponentSize md 크기 토큰 (위젯 파라미터)
textFieldStyle CoreTextFieldStyle? null chrome / dimensional / cursor / nested slot 묶음
prefix / suffix Widget? / Component? null 입력 영역 좌/우 슬롯
controller / focusNode / statesController platform-specific null 외부 컨트롤러

변형 (Variants)#

variant는 errorText 파라미터로 자동 결정됩니다:

  • errorText가 null이면 → 기본 스타일
  • errorText가 설정되면 → 에러 스타일 (빨간 보더)
// 기본 상태
CoTextField(
  placeholder: Text('이름을 입력하세요'),
)

// 에러 상태 (errorText 설정 시 자동)
CoTextField(
  errorText: '필수 입력 항목입니다',
  placeholder: Text('이름을 입력하세요'),
)

// 비활성화
CoTextField(
  enabled: false,
  placeholder: Text('수정 불가'),
)

// 읽기 전용
CoTextField(
  readOnly: true,
  initialValue: '읽기 전용 값',
)

동작 스펙 (Behavior)#

포커스 관리#

  • 클릭 또는 Tab 키로 포커스 획득 시 테두리 색상 변경
  • autofocus: true로 페이지 로드 시 자동 포커스

상태 전환#

  • defaultfocused (포커스 획득) → filled (값 입력) → default (포커스 해제)
  • error: errorText 설정 시 테두리와 에러 메시지 표시
  • disabled: 모든 인터랙션 비활성화, 흐리게 표시
  • readOnly: 포커스는 가능하나 편집 불가

사용 가이드라인 (Usage Guidelines)#

✅ Do#

항상 라벨과 함께 사용하세요.

CoTextField(
  label: '이메일 주소',
  placeholder: Text('user@example.com'),
)

라벨은 입력 필드의 용도를 명확히 하고, 접근성을 향상시킵니다.


❌ Don't#

placeholder만으로 라벨을 대체하지 마세요.

CoTextField(
  placeholder: Text('이메일 주소'),
)

입력을 시작하면 placeholder가 사라져 필드 용도를 알 수 없게 됩니다.

✅ Do#

에러 메시지는 구체적으로 작성하세요.

CoTextField(
  errorText: '이메일 형식이 올바르지 않습니다 (예: user@example.com)',
)

사용자가 무엇을 수정해야 하는지 바로 알 수 있습니다.


❌ Don't#

모호한 에러 메시지를 사용하지 마세요.

CoTextField(
  errorText: '입력이 올바르지 않습니다',
)

사용자가 무엇을 고쳐야 하는지 알 수 없습니다.

접근성 (Accessibility)#

키보드 인터랙션#

동작
Tab다음 입력 필드로 이동
Shift+Tab이전 입력 필드로 이동
Enter폼 제출 (단일 라인) 또는 줄바꿈 (다중 라인)
Escape포커스 해제

스크린 리더#

  • Flutter: Semantics로 라벨, 힌트, 에러 상태 자동 전달
  • Web: <input> 요소의 네이티브 접근성. label 속성으로 접근성 라벨 자동 연결

터치 타겟#

  • 최소 터치 타겟 높이: 48dp
  • 충분한 내부 패딩으로 터치 영역 확보
  • Form: 여러 입력 필드를 묶어 유효성 검증 수행
  • Select: 미리 정해진 옵션 중 선택
  • TextArea: 여러 줄 텍스트 입력