OptionList | CoUI
본문으로 건너뛰기
LogoCoUI

OptionList

선택지 목록 — OptionItem 을 세로로 쌓는 묶음

목차

OptionList 는 OptionItem 을 세로로 쌓은 선택지 목록입니다. 목록이 맡는 것은 배치뿐입니다 — 줄을 위에서 아래로 놓고, 각 줄이 목록의 폭을 채웁니다. 어느 줄이 골라졌는지, 하나만 고를 수 있는지 여럿을 고를 수 있는지는 호출자의 몫이고, 각 줄이 자기 selection 과 selected 를 말합니다.

기본 사용법 (Basic Usage)#

OptionList(
  children: [
    OptionItem(
      label: const Text('최신순'),
      selected: sort == Sort.latest,
      onPressed: () => setState(() => sort = Sort.latest),
    ),
    OptionItem(
      label: const Text('인기순'),
      selected: sort == Sort.popular,
      onPressed: () => setState(() => sort = Sort.popular),
    ),
  ],
)
OptionList(
  children: [
    OptionItem(
      label: const Text('최신순'),
      selected: sort == Sort.latest,
      onPressed: () => setState(() => sort = Sort.latest),
    ),
    OptionItem(
      label: const Text('인기순'),
      selected: sort == Sort.popular,
      onPressed: () => setState(() => sort = Sort.popular),
    ),
  ],
)

폭은 부모가 정합니다 — 목록은 받은 폭을 채우고 높이는 줄의 합이 정합니다.

Live Preview#

Web
최신순
인기순
낮은 가격순
Flutter
Loading Flutter...
코드 보기
class OptionListDefaultExample extends StatelessComponent {
  const OptionListDefaultExample({super.key});

  @override
  Component build(BuildContext context) {
    return OptionList(
      children: [
        OptionItem(
          label: const Text('최신순'),
          selected: true,
          onPressed: () {},
        ),
        OptionItem(
          label: const Text('인기순'),
          onPressed: () {},
        ),
        OptionItem(
          label: const Text('낮은 가격순'),
          onPressed: () {},
        ),
      ],
    );
  }
}
class OptionListDefaultExample extends StatelessWidget {
  const OptionListDefaultExample({super.key});

  @override
  Widget build(BuildContext context) {
    return OptionList(
      children: [
        OptionItem(
          label: const Text('최신순'),
          selected: true,
          onPressed: () {},
        ),
        OptionItem(
          label: const Text('인기순'),
          onPressed: () {},
        ),
        OptionItem(
          label: const Text('낮은 가격순'),
          onPressed: () {},
        ),
      ],
    );
  }
}

구조 (Anatomy)#

 [ OptionItem ]
 [ OptionItem ]
 [ OptionItem ]
부분역할슬롯
줄선택지 한 줄 — OptionItemchildren

Figma: CoUI 라이브러리 파일의 OptionList 페이지

사용 시기 (When to Use)#

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

  • 바텀시트나 패널 안에서 OptionItem 여러 줄을 한 목록으로 놓을 때

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

  • Menu: 동작을 실행하는 줄의 목록일 때
  • Select: 목록을 트리거의 드롭다운 안에 접어 둘 때

Props / Parameters#

W 는 Flutter의 Widget / Web의 Jaspr Component입니다.

속성타입기본값설명
children List<W> 필수 위에서 아래로 쌓을 줄 — 보통 OptionItem
optionListStyle CoreOptionListStyle? null 모든 chrome 이 지나는 단일 슬롯 (아래 표 참고)

CoreOptionListStyle 필드#

필드타입설명
spacing double? Space between two rows (logical px, pre-scaling). null → [defaultSpacing].

스타일 시스템 (Style System)#

optionListStyle#

  • 시맨틱 / behaviour: 위젯 파라미터로 직접 (children)
  • chrome / dimensional: CoreOptionListStyle 한 곳으로
부분필드기본값
간격spacing0 — 줄 사이

줄 사이 간격은 간격 사다리 위의 값이라 밀도(CoreDensity)가 한 칸 옮깁니다.

Resolve chain

CoreOptionListStyle.defaultX                 // 기본값
  → CoreOptionListTheme.style                // 프로젝트 공통
  → widget.optionListStyle                   // 인스턴스별

사용 예

CoreComponentTheme(
  optionList: CoreOptionListTheme(
    style: CoreOptionListStyle(spacing: CoreSpace.space4),
  ),
)

빠른 오버라이드 (Chain)

OptionList(children: items).withStyle(
  const CoreOptionListStyle(spacing: CoreSpace.space4),
)

동작 스펙 (Behavior)#

배치#

  • 줄을 위에서 아래로 쌓고, 줄 사이는 spacing 입니다. 각 줄은 목록의 폭을 채웁니다
  • 높이는 줄의 합이 정합니다

인터랙션#

  • 목록 자체는 입력을 받지 않습니다 — 탭은 각 줄(OptionItem)이 받습니다
  • 선택을 한 줄로 제한하는 규칙은 없습니다. 단일 선택이면 호출자가 한 줄만 selected 로 둡니다

상태 전환#

  • 상태가 없습니다. 같은 줄 · 같은 폭이면 같은 배치입니다

애니메이션#

  • 없습니다

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

Do#

한 목록의 줄은 같은 selection 으로 맞춘다

OptionList(
  children: [
    for (final tag in tags)
      OptionItem(
        selection: .multiple,
        label: Text(tag.name),
        selected: chosen.contains(tag),
        onPressed: () => toggle(tag),
      ),
  ],
)

이유: 한 목록에서 체크 표시와 체크박스가 섞이면 고르는 방식이 줄마다 달라 보입니다.

Don't#

줄 사이에 구분선을 직접 끼우지 않는다

// ❌ 줄마다 위아래 여백이 이미 14 라 구분선을 끼우면 목록이 늘어진다
OptionList(children: [first, const Divider(), second])

이유: 목록의 줄 사이 간격은 optionListStyle.spacing 으로 정합니다.

접근성 (Accessibility)#

역할#

  • Flutter: CoUISemantics(role: .group, container: true) 로 목록 전체를 하나의 그룹으로 묶습니다
  • Web: 루트 <div> 에 role="group" 을 내보냅니다

그룹에는 접근 가능한 이름이 없습니다. 줄의 라벨이 곧 내용입니다.

키보드#

키FlutterWeb
Tab활성 줄을 화면 순서로 이동활성 줄을 화면 순서로 이동
Enter / Space포커스를 가진 줄이 처리포커스를 가진 줄이 처리

목록 자체는 포커스를 받지 않습니다.

스크린 리더#

그룹 안에서 줄 순서대로 읽힙니다. 상태 통보(live region)는 없습니다.

포커스 관리#

  • 진입 · 이탈 · 트랩: 없음 — 목록은 오버레이가 아닙니다

알려진 제약#

  • 그룹에 이름이 없습니다. 이름이 필요하면 호출자가 붙입니다 — Web 은 attributes: {'aria-label': '…'}, Flutter 는 목록을 Semantics(label:) 로 감쌉니다

전역 축(동작 줄이기 · 고대비 · 색 강제 모드 · 시맨틱 역할 · 최소 터치 타겟)은 접근성 페이지가 한 곳에서 다룹니다.

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

항목FlutterWeb
클래스명OptionListOptionList
루트 CoUISemantics → Column(stretch, 최소 높이) <div role="group">(flex 열)
Web 전용 HTML 표면 — attributes · classes · css · eventHandlers · id 통과

위 차이 밖에는 없습니다 — 이름 · 순서 · 기본값을 두 위젯 생성자에서 대조했습니다.

  • OptionItem: 목록의 줄
  • Menu: 동작을 실행하는 줄의 목록
  • Select: 목록을 드롭다운 안에 접는 선택