RadioGroup | CoUI

RadioGroup

여러 항목 중 하나를 선택하는 라디오 버튼 그룹 컴포넌트

RadioGroup#

여러 항목 중 하나만 선택할 수 있는 라디오 버튼 그룹 컴포넌트입니다.

Live Preview#

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

  @override
  State<RadioDefaultExample> createState() => _RadioDefaultExampleState();
}

class _RadioDefaultExampleState extends State<RadioDefaultExample> {
  String? _selected = 'option1';

  @override
  Component build(BuildContext context) {
    return CoRadioGroup(
      value: _selected,
      onChanged: (v) => setState(() => _selected = v),
      options: const [
        (value: 'option1', label: 'Option 1'),
        (value: 'option2', label: 'Option 2'),
        (value: 'option3', label: 'Option 3'),
      ],
    );
  }
}
class RadioDefaultExample extends StatefulWidget {
  const RadioDefaultExample({super.key});

  @override
  State<RadioDefaultExample> createState() => _RadioDefaultExampleState();
}

class _RadioDefaultExampleState extends State<RadioDefaultExample> {
  String? _selected = 'option1';

  @override
  Widget build(BuildContext context) {
    return CoRadioGroup<String>(
      value: _selected,
      onChanged: (v) => setState(() => _selected = v),
      options: const [
        (value: 'option1', label: 'Option 1'),
        (value: 'option2', label: 'Option 2'),
        (value: 'option3', label: 'Option 3'),
      ],
    );
  }
}

사용 시기 (When to Use)#

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

  • 상호 배타적인 선택지 중 하나를 선택해야 하는 경우 (예: 성별, 결제 방법)
  • 선택지가 2~6개 이하로 모든 옵션을 동시에 표시해야 하는 경우
  • 사용자가 선택지를 비교하며 결정해야 하는 경우

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

  • Select: 선택지가 7개 이상이어서 드롭다운으로 공간을 절약해야 하는 경우
  • Checkbox: 여러 항목을 동시에 선택할 수 있는 경우
  • SwitchField: 켜기/끄기의 이진 선택인 경우

기본 사용법 (Basic Usage)#

// options 기반 간편 사용
CoRadioGroup(
  value: selectedGender,
  onChanged: handleGenderChanged,
  options: [
    (value: 'male', label: '남성'),
    (value: 'female', label: '여성'),
    (value: 'other', label: '기타'),
  ],
)

// 수평 배치
CoRadioGroup(
  value: selectedSize,
  onChanged: handleSizeChanged,
  orientation: CoreRadioGroupOrientation.horizontal,
  options: [
    (value: 'sm', label: 'S'),
    (value: 'md', label: 'M'),
    (value: 'lg', label: 'L'),
    (value: 'xl', label: 'XL'),
  ],
)

// child 기반 커스텀 레이아웃
CoRadioGroup(
  value: selectedOption,
  onChanged: handleOptionChanged,
  child: Column(
    children: [
      CoRadio(value: 'option1', label: 'Option 1'),
      CoRadio(value: 'option2', label: 'Option 2'),
    ],
  ),
)
// options 기반 간편 사용
CoRadioGroup(
  value: selectedGender,
  onChanged: handleGenderChanged,
  options: [
    (value: 'male', label: '남성'),
    (value: 'female', label: '여성'),
    (value: 'other', label: '기타'),
  ],
)

// 수평 레이아웃
CoRadioGroup(
  value: selectedSize,
  onChanged: handleSizeChanged,
  orientation: CoreRadioGroupOrientation.horizontal,
  options: [
    (value: 'sm', label: 'S'),
    (value: 'md', label: 'M'),
    (value: 'lg', label: 'L'),
  ],
)

// child 기반 커스텀 레이아웃
CoRadioGroup(
  value: selectedOption,
  onChanged: handleOptionChanged,
  child: div([
    CoRadio(value: 'option1', groupValue: selectedOption, label: 'Option 1', onChanged: handleOptionChanged),
    CoRadio(value: 'option2', groupValue: selectedOption, label: 'Option 2', onChanged: handleOptionChanged),
  ]),
)

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

CoRadioGroup 의 indicator chrome / 슬롯 미세 조정은 단일 radioGroupStyle (CoreRadioGroupStyle) 으로 흐릅니다. 시맨틱 enum (variant, size, orientation) 은 위젯 파라미터.

CoRadioGroup<String>(
  variant: CoreRadioGroupVariant.defaultVariant,
  size: CoreComponentSize.md,
  orientation: CoreRadioGroupOrientation.vertical,
  value: selected,
  options: [
    (value: 'apple', label: '사과'),
    (value: 'banana', label: '바나나'),
  ],
  onChanged: handleChange,
  radioGroupStyle: CoreRadioGroupStyle(
    indicatorBorderColor: cs.outline,
    indicatorActiveColor: cs.primary,
    gap: 8,
    itemGap: 12,
    indicatorDotStyle: CoreIconStyle(size: 8, color: cs.onPrimary),
    labelStyle: CoreTextStyle(fontWeight: CoreFontWeight.semiBold),
  ),
)

CoreRadioGroupStyle 필드#

필드타입설명
indicatorBackgroundColor Color? / String? unselected 배경
indicatorActiveColor Color? / String? selected 배경
indicatorBorderColor Color? / String? 보더 색
indicatorBorderWidthdouble?보더 두께
indicatorSizedouble?인디케이터 크기 (width = height)
gapdouble?인디케이터 ↔ 레이블 간격
itemGapdouble?형제 라디오 아이템 간 간격
indicatorDotStyle CoreIconStyle? 안쪽 dot (size/color)
labelStyleCoreTextStyle?아이템 레이블

Props / Parameters#

속성타입기본값설명
valueT?null현재 선택된 값
onChanged ValueChanged<T?>? null 선택 변경 콜백
options List<({T value, String label})>? null 간편 옵션 목록
child Widget? / Component? null 커스텀 레이아웃
variant CoreRadioGroupVariant defaultVariant 시맨틱 변형 (위젯 파라미터)
size CoreComponentSize md 크기 토큰 (위젯 파라미터)
orientation CoreRadioGroupOrientation vertical 배치 방향 (위젯 파라미터)
radioGroupStyle CoreRadioGroupStyle? null indicator chrome / nested slot 묶음
enabledbool?true활성화 여부
nameString?null폼 필드 이름

변형 (Variants)#

수직 배치 (기본)#

CoRadioGroup(
  value: selectedPlan,
  onChanged: handlePlanChanged,
  options: [
    (value: 'basic', label: '베이직'),
    (value: 'pro', label: '프로'),
    (value: 'enterprise', label: '엔터프라이즈'),
  ],
)

수평 배치#

CoRadioGroup(
  value: selectedPayment,
  onChanged: handlePaymentChanged,
  orientation: CoreRadioGroupOrientation.horizontal,
  options: [
    (value: 'card', label: '카드'),
    (value: 'transfer', label: '계좌이체'),
    (value: 'phone', label: '휴대폰'),
  ],
)

동작 스펙 (Behavior)#

인터랙션#

  • 클릭/탭: 라디오 버튼 또는 레이블 클릭 시 해당 옵션 선택
  • 호버: 포인터 오버 시 시각적 피드백 표시
  • 포커스: 포커스 링(focus ring)으로 현재 포커스된 항목 명확히 표시

상태 전환#

  • unselected -> selected (클릭 또는 Space/Enter 입력 시)
  • 이미 선택된 항목은 클릭해도 unselected로 변경되지 않음 (단일 선택 보장)
  • disabled 상태의 개별 항목은 선택 불가

그룹 동작#

  • 동일 그룹 내에서는 하나의 항목만 선택 가능
  • 선택 변경 시 이전 선택 항목은 자동으로 해제

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

Do#

선택지를 모두 명확하게 표시

CoRadioGroup(
  value: selectedDelivery,
  onChanged: handleDeliveryChanged,
  options: [
    (value: 'standard', label: '일반 배송 (3-5일)'),
    (value: 'express', label: '빠른 배송 (1-2일)'),
    (value: 'same_day', label: '당일 배송'),
  ],
)

모든 옵션을 한 번에 볼 수 있어 사용자가 신중하게 비교하고 선택할 수 있다.


Don't#

선택지가 많을 때 CoRadioGroup 사용

// 10개 이상의 옵션은 화면을 과하게 차지함
CoRadioGroup(
  value: selectedCountry,
  onChanged: handleCountryChanged,
  options: allCountries.map((c) => (value: c.code, label: c.name)).toList(),
  // 200여 개 국가 - Select를 사용해야 함
)

선택지가 많으면 스크롤이 필요해 비교가 어렵고 화면을 과도하게 차지한다.

접근성 (Accessibility)#

키보드 인터랙션#

동작
Tab그룹 내 다음 라디오로 이동
Space현재 포커스된 항목 선택

스크린 리더#

  • Flutter: Semanticsradiobutton role, checked 상태, 그룹 레이블 전달
  • Web: role="radiogroup" / role="radio", aria-checked 자동 적용

터치 타겟#

  • 최소 터치 타겟 크기: 48x48dp
  • 라디오 버튼 + 레이블 전체 영역이 터치 가능 영역

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

항목FlutterWeb
클래스명CoRadioGroup<T>CoRadioGroup
아이템CoRadio<T>CoRadio
제네릭T (any type)String only
childWidget? childComponent? child
색상 타입Color?CoreColor?
  • Checkbox: 여러 항목을 동시에 선택할 수 있는 경우
  • Select: 선택지가 많아 드롭다운이 필요한 경우
  • SwitchField: 켜기/끄기 이진 상태를 표현하는 경우

조합 예제#

// CoRadioGroup + Fieldset 조합: 요금제 선택 폼
Fieldset(
  legend: '요금제',
  description: '월 단위로 청구됩니다',
  children: [
    CoRadioGroup(
      value: selectedPlan,
      onChanged: handlePlanChanged,
      options: [
        (value: 'free', label: '무료 (5GB)'),
        (value: 'pro', label: '프로 (100GB) - 9,900원/월'),
        (value: 'business', label: '비즈니스 (무제한) - 29,900원/월'),
      ],
    ),
  ],
)