Toggle#
켜기/끄기 상태를 전환하는 토글 스위치 컴포넌트입니다.
Live Preview#
class ToggleDefaultExample extends StatefulComponent {
const ToggleDefaultExample({super.key});
@override
State<ToggleDefaultExample> createState() => _ToggleDefaultExampleState();
}
class _ToggleDefaultExampleState extends State<ToggleDefaultExample> {
bool _value = false;
@override
Component build(BuildContext context) {
return CoToggle(
value: _value,
label: 'Toggle',
onChanged: (v) => setState(() => _value = v),
);
}
}
class ToggleDefaultExample extends StatefulWidget {
const ToggleDefaultExample({super.key});
@override
State<ToggleDefaultExample> createState() => _ToggleDefaultExampleState();
}
class _ToggleDefaultExampleState extends State<ToggleDefaultExample> {
bool _value = false;
@override
Widget build(BuildContext context) {
return CoToggle(
value: _value,
onChanged: (v) => setState(() => _value = v),
label: 'Toggle',
);
}
}
class ToggleOnExample extends StatefulComponent {
const ToggleOnExample({super.key});
@override
State<ToggleOnExample> createState() => _ToggleOnExampleState();
}
class _ToggleOnExampleState extends State<ToggleOnExample> {
bool _value = true;
@override
Component build(BuildContext context) {
return CoToggle(
value: _value,
label: 'Toggle',
onChanged: (v) => setState(() => _value = v),
);
}
}
class ToggleOnExample extends StatefulWidget {
const ToggleOnExample({super.key});
@override
State<ToggleOnExample> createState() => _ToggleOnExampleState();
}
class _ToggleOnExampleState extends State<ToggleOnExample> {
bool _value = true;
@override
Widget build(BuildContext context) {
return CoToggle(
value: _value,
onChanged: (v) => setState(() => _value = v),
label: 'Toggle',
);
}
}
class ToggleDisabledExample extends StatefulComponent {
const ToggleDisabledExample({super.key});
@override
State<ToggleDisabledExample> createState() => _ToggleDisabledExampleState();
}
class _ToggleDisabledExampleState extends State<ToggleDisabledExample> {
bool _value = false;
@override
Component build(BuildContext context) {
return CoToggle(
value: _value,
label: 'Toggle',
enabled: false,
onChanged: (value) {
setState(() {
_value = value;
});
},
);
}
}
class ToggleDisabledExample extends StatefulWidget {
const ToggleDisabledExample({super.key});
@override
State<ToggleDisabledExample> createState() => _ToggleDisabledExampleState();
}
class _ToggleDisabledExampleState extends State<ToggleDisabledExample> {
bool _value = false;
@override
Widget build(BuildContext context) {
return CoToggle(
value: _value,
enabled: false,
label: 'Toggle',
onChanged: (value) {
setState(() {
_value = value;
});
},
);
}
}
사용 시기 (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 색 |
trackBorderRadius | double? | 트랙 보더 반경 |
trackWidth / trackHeight |
double? |
트랙 크기 |
thumbActiveColor / thumbInactiveColor |
Color? / String? |
thumb on/off 색 |
thumbSize / thumbBorderRadius |
double? |
thumb 크기 / 반경 |
gap | double? | 토글 ↔ 레이블 간격 |
labelStyle | CoreTextStyle? | label 텍스트 스타일 |
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
value | bool | 필수 | 현재 상태 |
onChanged |
ValueChanged<bool>? |
null |
상태 변경 콜백 |
label | String? | null | 라벨 텍스트 |
variant |
CoreToggleVariant |
defaultVariant |
시맨틱 변형 (위젯 파라미터) |
size |
CoreComponentSize |
md |
크기 토큰 (위젯 파라미터) |
enabled | bool? | true | 활성화 여부 |
prefix |
Widget? |
null |
토글 앞 위젯 (Flutter) |
suffix |
Widget? |
null |
토글 뒤 위젯 (Flutter) |
toggleStyle |
CoreToggleStyle? |
null |
track + thumb chrome / nested slot 묶음 |
name | String? | 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)#
상태 전환#
- 클릭/탭 시
value가true↔false토글 enabled: false면 인터랙션 비활성 + 불투명도 감소
상태 관리#
// 직접 콜백 방식
CoToggle(
value: isDarkMode,
onChanged: (value) => setState(() => isDarkMode = value),
)
애니메이션#
-
Flutter: 100ms,
Curves.easeInOut- 트랙 색상:
AnimatedContainer로 활성/비활성 색상 전환 - 썸네일 이동:
AnimatedPositioned로 좌우 슬라이딩
- 트랙 색상:
- Web: CSS
transition-transform으로 썸네일 위치 전환
크기 토큰#
| Size | Track | Thumb | Flutter | Web |
|---|---|---|---|---|
| xs | 32×16 | 12 | CoreToggleSizeTokens | Tailwind |
| sm | 40×20 | 16 | CoreToggleSizeTokens | Tailwind |
| md | 44×24 | 20 | CoreToggleSizeTokens | Tailwind |
| lg | 56×28 | 24 | CoreToggleSizeTokens | Tailwind |
| xl | 64×32 | 28 | CoreToggleSizeTokens | Tailwind |
사용 가이드라인 (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-ringCSS 포커스 링
비활성 상태#
- Flutter:
Opacity(0.5)로 시각적 구분 - Web:
disabled속성 +cursor-not-allowed opacity-50CSS
크로스 플랫폼 차이점 (Platform Differences)#
CoToggle은
coui_core의CoreToggleVariantStyle과CoreToggleSizeTokens를 사용하여 Flutter/Web 동일한 디자인 토큰 기반으로 렌더링됩니다.
| 항목 | Flutter | Web |
|---|---|---|
| 애니메이션 | 100ms easeInOut (AnimatedPositioned) |
CSS transition-transform |
| 크기 토큰 | CoreToggleSizeTokens.styles (pixel) |
CoreToggleSizeTokens.scale (Tailwind) |
| 색상 토큰 | CoreToggleVariantStyle.styles (ColorResolver) |
CoreToggleVariantStyle.scale (Tailwind) |
| 커스터마이징 | activeColor, prefix, suffix |
label |
| ARIA | Flutter 플랫폼 시맨틱 | role="switch", aria-checked |
| 폼 연동 | FormValueSupplier 믹스인 | hidden <input> |
관련 컴포넌트 (Related Components)#
- 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,
),
]),
)