Toggle | CoUI

Toggle

토글 스위치 컴포넌트

Toggle#

켜기/끄기 상태를 전환하는 토글 스위치 컴포넌트입니다.

Live Preview#

사용 시기 (When to Use)#

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

  • 즉시 적용되는 on/off 설정을 전환할 때 (다크 모드, 알림 등)
  • 두 가지 상태만 있고 변경이 바로 반영될 때
  • 설정 목록에서 각 옵션을 개별적으로 토글할 때

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

  • Checkbox: 폼 제출 후에 반영되는 선택일 때
  • Select: 두 가지 이상의 옵션 중 선택할 때
  • Button: 단순 액션 트리거일 때 (상태 보존 불필요)

기본 사용법 (Basic Usage)#

// 기본 토글
CoToggle(
  value: isDarkMode,
  onChanged: handleDarkModeToggle,
)

// 라벨 텍스트
CoToggle(
  value: isNotificationEnabled,
  onChanged: handleNotificationToggle,
  label: '알림 수신',
)

// 앞에 라벨 Widget 배치
CoToggle(
  value: isActive,
  onChanged: handleToggle,
  prefix: Text('자동 저장'),
)

// 뒤에 라벨 Widget 배치
CoToggle(
  value: isActive,
  onChanged: handleToggle,
  suffix: Text('자동 저장'),
)

// 비활성화
CoToggle(
  value: false,
  onChanged: null,
  enabled: false,
  label: '비활성 옵션',
)
// 기본 토글
CoToggle(
  value: isDarkMode,
  onChanged: handleDarkModeToggle,
)

// 라벨 텍스트
CoToggle(
  value: isNotificationEnabled,
  onChanged: handleNotificationToggle,
  label: '알림 수신',
)

// 비활성화
CoToggle(
  value: false,
  enabled: false,
)

// 초기 상태 on
CoToggle(
  value: true,
  onChanged: handleToggle,
)

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

CoToggle 의 track + thumb chrome / 슬롯 미세 조정은 단일 toggleStyle (CoreToggleStyle) 으로 흐릅니다. 시맨틱 enum (variant, size) 은 위젯 파라미터.

CoToggle(
  variant: CoreToggleVariant.defaultVariant,
  size: CoreComponentSize.md,
  value: enabled,
  onChanged: handleChange,
  label: '알림 받기',
  toggleStyle: CoreToggleStyle(
    trackActiveColor: cs.primary,
    trackInactiveColor: cs.outline,
    thumbActiveColor: cs.onPrimary,
    thumbInactiveColor: cs.onSurface,
    trackBorderRadius: 12,
    gap: 8,
    labelStyle: CoreTextStyle(fontWeight: CoreFontWeight.semiBold),
  ),
)

CoreToggleStyle 필드 (track + thumb chrome 평면 + nested label)#

필드타입설명
trackActiveColor / trackInactiveColor Color? / String? 트랙 on/off 색
trackBorderRadiusdouble?트랙 보더 반경
trackWidth / trackHeight double? 트랙 크기
thumbActiveColor / thumbInactiveColor Color? / String? thumb on/off 색
thumbSize / thumbBorderRadius double? thumb 크기 / 반경
gapdouble?토글 ↔ 레이블 간격
labelStyleCoreTextStyle?label 텍스트 스타일

Props / Parameters#

속성타입기본값설명
valuebool필수현재 상태
onChanged ValueChanged<bool>? null 상태 변경 콜백
labelString?null라벨 텍스트
variant CoreToggleVariant defaultVariant 시맨틱 변형 (위젯 파라미터)
size CoreComponentSize md 크기 토큰 (위젯 파라미터)
enabledbool?true활성화 여부
prefix Widget? null 토글 앞 위젯 (Flutter)
suffix Widget? null 토글 뒤 위젯 (Flutter)
toggleStyle CoreToggleStyle? null track + thumb chrome / nested slot 묶음
nameString?null폼 제출 이름

변형 (Variants)#

크기#

CoToggle(value: v1, onChanged: handle, size: CoreComponentSize.sm)
CoToggle(value: v2, onChanged: handle, size: CoreComponentSize.md)
CoToggle(value: v3, onChanged: handle, size: CoreComponentSize.lg)

설정 목록에서 사용#

Column(
  children: [
    CoToggle(
      value: settings.darkMode,
      onChanged: handleDarkMode,
      label: '다크 모드',
    ),
    CoToggle(
      value: settings.notifications,
      onChanged: handleNotifications,
      label: '푸시 알림',
    ),
    CoToggle(
      value: settings.autoSave,
      onChanged: handleAutoSave,
      label: '자동 저장',
    ),
  ],
)

동작 스펙 (Behavior)#

상태 전환#

  • 클릭/탭 시 valuetruefalse 토글
  • enabled: false면 인터랙션 비활성 + 불투명도 감소

상태 관리#

// 직접 콜백 방식
CoToggle(
  value: isDarkMode,
  onChanged: (value) => setState(() => isDarkMode = value),
)

애니메이션#

  • Flutter: 100ms, Curves.easeInOut
    • 트랙 색상: AnimatedContainer로 활성/비활성 색상 전환
    • 썸네일 이동: AnimatedPositioned로 좌우 슬라이딩
  • Web: CSS transition-transform으로 썸네일 위치 전환

크기 토큰#

SizeTrackThumbFlutterWeb
xs32×1612CoreToggleSizeTokensTailwind
sm40×2016CoreToggleSizeTokensTailwind
md44×2420CoreToggleSizeTokensTailwind
lg56×2824CoreToggleSizeTokensTailwind
xl64×3228CoreToggleSizeTokensTailwind

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

✅ Do#

토글에 항상 라벨을 제공하세요.

CoToggle(
  value: isDarkMode,
  onChanged: handleDarkMode,
  label: '다크 모드',
)

토글의 목적이 명확해집니다.


❌ Don't#

라벨 없이 토글만 배치하지 마세요.

CoToggle(
  value: isDarkMode,
  onChanged: handleDarkMode,
)

사용자가 무엇을 켜고 끄는지 알 수 없습니다.

✅ Do#

즉시 적용되는 설정에 토글을 사용하세요.

CoToggle(
  value: notifications,
  onChanged: (value) {
    updateNotificationSetting(value); // 즉시 서버 반영
  },
  label: '푸시 알림',
)

토글 변경 = 즉시 적용이 사용자의 기대입니다.


❌ Don't#

"저장" 버튼이 필요한 설정에 토글을 사용하지 마세요.

저장 전까지 실제 반영이 안 되면 사용자가 혼란스러워합니다. 이 경우 Checkbox가 더 적합합니다.

✅ Do#

긍정형 라벨로 on 상태를 명확히 하세요.

CoToggle(label: '알림 받기', ...)
CoToggle(label: '자동 저장', ...)

on = 활성화가 직관적입니다.


❌ Don't#

부정형 라벨을 사용하지 마세요.

CoToggle(label: '알림 끄기', ...)

켜면 "알림 끄기"가 활성화되어 이중 부정이 됩니다.

접근성 (Accessibility)#

키보드 인터랙션#

동작
Space토글 전환
Enter토글 전환
Tab다음 요소로 포커스 이동

스크린 리더#

  • Flutter: Clickable 위젯으로 플랫폼 시맨틱 전달. 포커스 시 라벨과 상태(켜짐/꺼짐) 읽힘
  • Web: role="switch" + aria-checked="true|false"로 스위치 역할 명시. 스크린 리더가 "스위치, 켜짐" 형태로 읽음

포커스 표시#

  • Flutter: Clickable 위젯의 포커스 링
  • Web: focus-visible:ring-2 focus-visible:ring-ring CSS 포커스 링

비활성 상태#

  • Flutter: Opacity(0.5)로 시각적 구분
  • Web: disabled 속성 + cursor-not-allowed opacity-50 CSS

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

CoToggle은 coui_coreCoreToggleVariantStyleCoreToggleSizeTokens를 사용하여 Flutter/Web 동일한 디자인 토큰 기반으로 렌더링됩니다.

항목FlutterWeb
애니메이션 100ms easeInOut (AnimatedPositioned) CSS transition-transform
크기 토큰 CoreToggleSizeTokens.styles (pixel) CoreToggleSizeTokens.scale (Tailwind)
색상 토큰 CoreToggleVariantStyle.styles (ColorResolver) CoreToggleVariantStyle.scale (Tailwind)
커스터마이징 activeColor, prefix, suffix label
ARIAFlutter 플랫폼 시맨틱role="switch", aria-checked
폼 연동FormValueSupplier 믹스인hidden <input>
  • Checkbox: 체크박스. "제출 후 적용"되는 선택에 적합 (Toggle은 즉시 적용)
  • Slider: 범위 값 조절. boolean이 아닌 연속 값일 때
  • Form: 폼 컨테이너. CoToggle을 폼 필드로 등록 가능

조합 예제#

// 설정 페이지 패턴
Drawer(
  title: '설정',
  child: Column(children: [
    CoToggle(
      value: settings.darkMode,
      onChanged: handleDarkMode,
      label: '다크 모드',
    ),
    CoToggle(
      value: settings.notifications,
      onChanged: handleNotifications,
      label: '푸시 알림',
    ),
    CoToggle(
      value: settings.autoSave,
      onChanged: handleAutoSave,
      label: '자동 저장',
    ),
    CoToggle(
      value: settings.analytics,
      onChanged: handleAnalytics,
      label: '사용 통계 수집',
      enabled: !settings.isGuest,
    ),
  ]),
)