ChipInput | CoUI

ChipInput

칩(태그) 형태로 여러 항목을 입력하고 관리하는 컴포넌트

ChipInput#

텍스트를 입력하고 Enter로 확인하면 칩(태그) 형태로 추가되는 다중 항목 입력 컴포넌트입니다.

Live Preview#

Web
Flutter
Loading Flutter...
ChipInput(
  chips: ['Flutter', 'Dart'],
  placeholder: 'Add tags...',
  onChanged: handleChipsChanged,
)
ChipInput.simple(
  chips: ['Flutter', 'Dart'],
  placeholder: 'Add tags...',
  onChanged: handleChipsChanged,
)

사용 시기 (When to Use)#

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

  • 태그, 이메일 주소, 키워드 등 여러 항목을 동적으로 추가/제거해야 하는 경우
  • 선택 개수가 가변적인 다중 입력이 필요한 경우
  • 입력한 값을 시각적으로 태그 형태로 확인하면서 관리해야 하는 경우

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

  • AutoComplete: 미리 정의된 목록에서 항목을 검색해 선택해야 하는 경우
  • Chip: 미리 정의된 칩 목록에서 토글 선택하는 경우
  • Select: 고정된 옵션 목록에서 다중 선택이 필요한 경우

기본 사용법 (Basic Usage)#

// 기본 칩 입력 (제네릭, chipBuilder 필수)
ChipInput<String>(
  chips: selectedTags,
  chipBuilder: (context, chip) => Text(chip),
  onChanged: handleTagsChanged,
  placeholder: Text('태그 입력 후 Enter'),
)

// 자동완성 제안 목록과 함께 사용
ChipInput<String>(
  chips: selectedSkills,
  chipBuilder: (context, chip) => Text(chip),
  suggestions: availableSkills,
  onChanged: handleSkillsChanged,
  onSuggestionChoosen: handleSuggestionChoosen,
  placeholder: Text('스킬 추가'),
)

// 컨트롤러 기반 상태 관리
ControlledChipInput<String>(
  chips: emailList,
  chipBuilder: (context, chip) => Text(chip),
  onChanged: handleEmailListChanged,
  placeholder: Text('이메일 추가'),
)
// 기본 칩 입력 (String 기반, 단순 API)
ChipInput(
  chips: selectedTags,
  onChanged: handleTagsChanged,
  placeholder: '태그 입력 후 Enter',
)

// 최대 개수 제한
ChipInput(
  chips: selectedSkills,
  onChanged: handleSkillsChanged,
  placeholder: '스킬 추가',
  maxChips: 5,
)

// 비활성화 상태
ChipInput(
  chips: emailList,
  onChanged: handleEmailListChanged,
  placeholder: '이메일 추가',
  enabled: false,
)

Props / Parameters#

속성타입기본값설명
valuesList<String>필수현재 칩 목록
onChanged ValueChanged<List<String>>? null 목록 변경 콜백
onAdd ValueChanged<String>? null 칩 추가 콜백
onRemove ValueChanged<String>? null 칩 제거 콜백
placeholder String? null 입력 필드 플레이스홀더
maxChipsint?null최대 칩 수
validator String? Function(String)? null 입력 유효성 검사 함수

변형 (Variants)#

태그 입력#

ChipInput(
  values: articleTags,
  onChanged: handleArticleTagsChanged,
  placeholder: '태그를 입력하세요',
  maxChips: 10,
)

이메일 수신자#

ChipInput(
  values: recipients,
  onChanged: handleRecipientsChanged,
  placeholder: '이메일 주소 추가',
  validator: validateEmail,
)

동작 스펙 (Behavior)#

인터랙션#

  • 추가: 텍스트 입력 후 Enter 키 또는 , 구분자로 칩 추가
  • 제거: 칩 내 × 버튼 클릭 또는 Backspace로 마지막 칩 제거
  • 편집 불가: 추가된 칩의 텍스트는 직접 수정 불가 (제거 후 재입력)
  • maxChips 도달: 최대 개수 도달 시 입력 필드 비활성화

상태 전환#

  • emptytyping (입력 필드 포커스 및 타이핑)
  • typingchip-added (Enter 또는 , 입력 시 칩 생성)
  • chip-addedtyping (계속 입력 가능)
  • invalid 상태: validator 실패 시 입력 필드에 에러 표시 (칩 생성 차단)
  • max-reached: maxChips 도달 시 입력 필드 disabled 처리

중복 처리#

  • 동일한 값의 칩은 기본적으로 추가되지 않음
  • 대소문자 구분 없이 중복 검사

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

✅ Do#

validator로 입력 형식을 검증

CouiChipInput(
  values: emailRecipients,
  onChanged: handleRecipientsChanged,
  placeholder: '수신자 이메일 추가',
  validator: (value) {
    final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
    if (!emailRegex.hasMatch(value)) return '유효한 이메일 주소를 입력하세요';
    return null;
  },
)

잘못된 형식의 값이 칩으로 추가되기 전에 검증하여 데이터 품질을 보장한다.


❌ Don't#

자동완성과 함께 미리 정해진 목록에서만 선택하는 경우에 사용

// ❌ 고정 목록에서 선택만 필요하다면 AutoComplete나 Select 사용
CouiChipInput(
  values: selectedCategories,
  onChanged: handleCategoriesChanged,
  // 자유 입력이 가능하여 유효하지 않은 카테고리가 추가될 수 있음
)

미리 정의된 옵션만 허용해야 한다면 AutoCompleteSelect를 사용하고 ChipInput은 자유 입력이 허용될 때 사용한다.

✅ Do#

maxChips와 함께 남은 입력 가능 수 안내

CouiChipInput(
  values: selectedSkills,
  onChanged: handleSkillsChanged,
  placeholder: '스킬 추가 (최대 5개)',
  maxChips: 5,
)

최대 개수를 명시하면 사용자가 미리 입력 계획을 세울 수 있다.


❌ Don't#

maxChips 없이 무제한 칩 추가 허용

// ❌ 제한 없이 너무 많은 칩이 추가되면 레이아웃이 깨질 수 있음
CouiChipInput(
  values: tags,
  onChanged: handleTagsChanged,
  placeholder: '태그 추가',
  // maxChips 미설정 - 수십 개 이상 추가 가능
)

서비스 요구사항에 맞는 적절한 최대 개수를 설정하여 UI 일관성을 유지한다.

✅ Do#

이미 추가된 항목은 중복 추가를 방지하세요.

CouiChipInput(
  allowDuplicate: false,  // 중복 입력 방지
  suggestions: allTags,
  onChanged: handleTagsChanged,
  label: '태그 추가',
)

중복 태그가 추가되면 데이터 정합성이 깨지고 사용자가 혼란을 겪습니다. 기본적으로 중복을 방지하세요.


❌ Don't#

최대 개수 제한 없이 무한 추가를 허용하지 마세요.

// ❌ maxChips 미설정으로 무한 추가 허용
CouiChipInput(
  suggestions: allItems,
  onChanged: handleItemsChanged,
  // maxChips 없음
)

제한 없이 항목을 추가할 수 있으면 UI가 넘치고 성능 문제가 발생할 수 있습니다. maxChips로 상한을 설정하세요.

접근성 (Accessibility)#

키보드 인터랙션#

동작
Enter현재 입력 텍스트를 칩으로 추가
,현재 입력 텍스트를 칩으로 추가 (구분자)
Backspace입력 필드가 비어 있을 때 마지막 칩 제거
Delete포커스된 칩 제거
/ 칩 간 포커스 이동
Tab다음 포커스 가능 요소로 이동

스크린 리더#

  • Flutter: Semantics로 각 칩에 값 + "삭제 버튼" 레이블 제공
  • Web: 각 칩에 role="option", aria-selected="true", 삭제 버튼에 aria-label="[값] 삭제" 자동 적용

터치 타겟#

  • 칩 삭제 버튼 최소 크기: 32x32dp (칩 내 여백 포함 44dp)
  • 입력 필드 최소 높이: 48dp

크로스 플랫폼 차이점 (Platform Differences)#

v3.0부터 기본 API (enabled, onChanged 등)가 통일되었습니다. 아래는 플랫폼 고유 차이점만 나열합니다.

항목FlutterWeb
클래스명CouiChipInputChipInput
칩 스타일CouiChip 위젯CSS 스타일 태그
  • Chip: 미리 정의된 칩 목록에서 선택/해제하는 경우
  • Input: 단일 텍스트 값만 입력받는 경우
  • AutoComplete: 입력 시 제안 목록과 함께 단일 항목을 선택하는 경우

조합 예제#

// ChipInput + AutoComplete 조합: 추천 태그와 함께 자유 입력
Column(
  children: [
    // 추천 태그를 자동완성으로 제공하면서 자유 입력도 허용
    CouiAutoComplete(
      suggestions: popularTags,
      onSelected: (tag) {
        handleTagsChanged([...currentTags, tag]);
      },
      labelText: '추천 태그 검색',
    ),
    CouiChipInput(
      values: currentTags,
      onChanged: handleTagsChanged,
      placeholder: '직접 태그 입력',
      maxChips: 10,
    ),
  ],
)