SwitchField#
라벨과 보조 설명이 포함된 폼 친화적인 스위치 컴포넌트입니다.
Live Preview#
class SwitchFieldDefaultExample extends StatefulComponent {
const SwitchFieldDefaultExample({super.key});
@override
State<SwitchFieldDefaultExample> createState() =>
_SwitchFieldDefaultExampleState();
}
class _SwitchFieldDefaultExampleState
extends State<SwitchFieldDefaultExample> {
bool _value = false;
@override
Component build(BuildContext context) {
return SwitchField(
label: 'Enable notifications',
description: 'Receive push notifications.',
value: _value,
onChanged: (v) => setState(() => _value = v),
);
}
}
class SwitchFieldDefaultExample extends StatefulWidget {
const SwitchFieldDefaultExample({super.key});
@override
State<SwitchFieldDefaultExample> createState() =>
_SwitchFieldDefaultExampleState();
}
class _SwitchFieldDefaultExampleState
extends State<SwitchFieldDefaultExample> {
bool _value = false;
@override
Widget build(BuildContext context) {
return SwitchField(
label: 'Enable notifications',
description: 'Receive push notifications.',
value: _value,
onChanged: (v) => setState(() => _value = v),
);
}
}
class SwitchFieldChainExample extends StatefulComponent {
const SwitchFieldChainExample({super.key});
@override
State<SwitchFieldChainExample> createState() => _SwitchFieldChainExampleState();
}
class _SwitchFieldChainExampleState extends State<SwitchFieldChainExample> {
bool _value = false;
@override
Component build(BuildContext context) {
return SwitchField(
label: 'Enable notifications',
description: 'Receive push notifications.',
value: _value,
onChanged: (v) => setState(() => _value = v),
).withStyle(
const CoreSwitchFieldStyle(
labelToggleGapStyle: CoreGapStyle(size: CoreSpace.space24),
labelDescriptionGapStyle: CoreGapStyle(size: CoreSpace.space4),
activeColor: CoreColor.token(CoreColors.success),
labelTextStyle: CoreTextStyle.token(
CoreTextStyles.titleSmall,
color: CoreColor.token(CoreColors.onSurface),
),
),
);
}
}
class SwitchFieldChainExample extends StatefulWidget {
const SwitchFieldChainExample({super.key});
@override
State<SwitchFieldChainExample> createState() => _SwitchFieldChainExampleState();
}
class _SwitchFieldChainExampleState extends State<SwitchFieldChainExample> {
bool _value = false;
@override
Widget build(BuildContext context) {
return SwitchField(
label: 'Enable notifications',
description: 'Receive push notifications.',
value: _value,
onChanged: (v) => setState(() => _value = v),
).withStyle(
const CoreSwitchFieldStyle(
labelToggleGapStyle: CoreGapStyle(size: CoreSpace.space24),
labelDescriptionGapStyle: CoreGapStyle(size: CoreSpace.space4),
activeColor: CoreColor.token(CoreColors.success),
labelTextStyle: CoreTextStyle.token(
CoreTextStyles.titleSmall,
color: CoreColor.token(CoreColors.onSurface),
),
),
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 설정 화면에서 기능을 켜거나 끄는 이진 상태를 제어하는 경우
- 라벨과 설명 텍스트가 함께 필요한 스위치 형태의 폼 필드
- 알림, 다크 모드, 자동 저장 등 즉각 효과가 있는 토글 설정
- 폼 내에서 체크박스보다 직관적인 ON/OFF 표현이 필요한 경우
대신 다른 컴포넌트를 사용하세요:
Toggle: 라벨 없이 단독으로 사용하는 간단한 스위치Checkbox: 여러 항목의 선택 여부를 폼 내에서 관리하는 경우RadioGroup: 상호 배타적인 여러 옵션 중 하나를 선택하는 경우
기본 사용법 (Basic Usage)#
// 기본 스위치 필드
SwitchField(
value: isNotificationEnabled,
onChanged: handleNotificationToggled,
label: '알림 수신',
)
// 설명 포함
SwitchField(
value: isDarkMode,
onChanged: handleDarkModeToggled,
label: '다크 모드',
description: '어두운 테마를 사용합니다',
)
// 큰 크기 — 크기는 chrome 이므로 switchFieldStyle 로 진입합니다
SwitchField(
value: isAutoSave,
onChanged: handleAutoSaveToggled,
label: '자동 저장',
description: '변경 사항을 자동으로 저장합니다',
switchFieldStyle: const CoreSwitchFieldStyle(size: CoreComponentSize.lg),
)
// 기본 스위치 필드
SwitchField(
value: isNotificationEnabled,
onChanged: handleNotificationToggled,
label: '알림 수신',
)
// 설명 텍스트 포함 — description으로 보조 설명 표시
SwitchField(
value: isDarkMode,
onChanged: handleDarkModeToggled,
label: '다크 모드',
description: '어두운 테마를 사용합니다',
)
// 비활성화 상태 — enabled: false로 상호작용 차단
SwitchField(
value: isAutoSave,
onChanged: handleAutoSaveToggled,
label: '자동 저장',
description: '변경 사항을 자동으로 저장합니다',
enabled: false,
)
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
value |
bool |
필수 (Flutter) / false (Web) |
현재 스위치 상태 |
onChanged |
ValueChanged<bool>? |
필수 (Flutter) / null (Web) |
상태 변경 콜백 |
label | String | 필수 | 스위치 라벨 |
description |
String? |
null |
보조 설명 텍스트 |
enabled | bool | true | 활성화 여부 |
switchFieldStyle |
CoreSwitchFieldStyle? |
null |
인스턴스별 chrome 단일 진입점 |
CoreSwitchFieldStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
size |
CoreComponentSize? |
Toggle size within the switch field. |
labelToggleGapStyle |
CoreGapStyle? |
Nested [CoreGapStyle] slot for the horizontal gap between the label section and the toggle. Forwarded to the
Gap
widget rendered between the two siblings on Flutter, and drives the root
<div>
's inline
gap
(via
size
) on Web. Merged on top of [defaultLabelToggleGapStyle].
|
labelDescriptionGapStyle |
CoreGapStyle? |
Nested [CoreGapStyle] slot for the vertical gap between the label and the description inside the label section. Forwarded to the
Gap
widget rendered between the two
Text
siblings on Flutter, and drives the text column
<div>
's inline
row-gap
(via
size
) on Web. Merged on top of [defaultLabelDescriptionGapStyle].
|
clickableStyle |
CoreClickableStyle? |
Nested [CoreClickableStyle] slot for the composed
Clickable
(press scale / durations / focus ring / disabled opacity). Merged on top of [defaultClickableStyle] and raw-forwarded.
|
activeColor | CoreColor? | Active (on) track colour. |
inactiveColor |
CoreColor? |
Inactive (off) track colour. |
labelTextStyle |
CoreTextStyle? |
Label text style when enabled (typography + colour). Merged with [defaultLabelTextStyle] by the resolver. |
disabledLabelTextStyle |
CoreTextStyle? |
Label text style when disabled. Merged with [defaultDisabledLabelTextStyle] by the resolver. |
descriptionTextStyle |
CoreTextStyle? |
Description text style when enabled. Merged with [defaultDescriptionTextStyle] by the resolver. |
disabledDescriptionTextStyle |
CoreTextStyle? |
Description text style when disabled. Merged with [defaultDisabledDescriptionTextStyle] by the resolver. |
빠른 오버라이드 (Chain)#
이미 만든 SwitchField 인스턴스에 스타일을 빠르게 덧붙이고 싶다면 withStyle 체인을 쓸 수 있습니다. 생성자의 style 슬롯 인자와 동일하게 동작하지만, 이미 구성된 위젯 위에서 바로 이어 쓸 수 있습니다.
class SwitchFieldChainExample extends StatefulWidget {
const SwitchFieldChainExample({super.key});
@override
State<SwitchFieldChainExample> createState() => _SwitchFieldChainExampleState();
}
class _SwitchFieldChainExampleState extends State<SwitchFieldChainExample> {
bool _value = false;
@override
Widget build(BuildContext context) {
return SwitchField(
label: 'Enable notifications',
description: 'Receive push notifications.',
value: _value,
onChanged: (v) => setState(() => _value = v),
).withStyle(
const CoreSwitchFieldStyle(
labelToggleGapStyle: CoreGapStyle(size: CoreSpace.space24),
labelDescriptionGapStyle: CoreGapStyle(size: CoreSpace.space4),
activeColor: CoreColor.token(CoreColors.success),
labelTextStyle: CoreTextStyle.token(
CoreTextStyles.titleSmall,
color: CoreColor.token(CoreColors.onSurface),
),
),
);
}
}
class SwitchFieldChainExample extends StatefulComponent {
const SwitchFieldChainExample({super.key});
@override
State<SwitchFieldChainExample> createState() => _SwitchFieldChainExampleState();
}
class _SwitchFieldChainExampleState extends State<SwitchFieldChainExample> {
bool _value = false;
@override
Component build(BuildContext context) {
return SwitchField(
label: 'Enable notifications',
description: 'Receive push notifications.',
value: _value,
onChanged: (v) => setState(() => _value = v),
).withStyle(
const CoreSwitchFieldStyle(
labelToggleGapStyle: CoreGapStyle(size: CoreSpace.space24),
labelDescriptionGapStyle: CoreGapStyle(size: CoreSpace.space4),
activeColor: CoreColor.token(CoreColors.success),
labelTextStyle: CoreTextStyle.token(
CoreTextStyles.titleSmall,
color: CoreColor.token(CoreColors.onSurface),
),
),
);
}
}
변형 (Variants)#
기본#
SwitchField(
value: emailMarketing,
onChanged: handleEmailMarketingToggled,
label: '마케팅 이메일 수신',
)
설명 포함#
SwitchField(
value: biometricLogin,
onChanged: handleBiometricToggled,
label: '생체 인증 로그인',
description: '지문 또는 얼굴 인식으로 로그인합니다',
)
동작 스펙 (Behavior)#
인터랙션#
- 클릭/탭: 스위치 토글 또는 레이블/설명 영역 클릭으로 상태 전환
- 호버: 포인터 오버 시 스위치에 시각적 강조 표시
- 포커스: 포커스 링으로 현재 포커스 상태 표시
상태 전환#
off→on(클릭 또는 Space/Enter 입력)on→off(다시 클릭 또는 Space/Enter 입력)disabled상태에서는 모든 인터랙션 무시
즉각 반영 vs 폼 제출#
- 설정 페이지에서 SwitchField는 일반적으로 클릭 즉시 효과 반영
- 폼 내에서 사용 시 다른 필드와 함께 제출 시 반영
사용 가이드라인 (Usage Guidelines)#
✅ Do#
description으로 스위치 효과를 명확히 설명
SwitchField(
value: locationTracking,
onChanged: handleLocationTrackingToggled,
label: '위치 정보 수집',
description: '앱 사용 중 위치 데이터를 수집하여 맞춤 서비스를 제공합니다',
)
스위치의 효과와 영향을 설명 텍스트로 명확히 전달하면 사용자가 안심하고 선택할 수 있다.
❌ Don't#
다중 선택이 필요한 경우에 SwitchField 사용
// ❌ 여러 항목 중 다수를 선택해야 하는 경우 Checkbox가 적합
Column(
children: [
SwitchField(value: receiveEmail, onChanged: handleEmailToggled, label: '이메일'),
SwitchField(value: receiveSms, onChanged: handleSmsToggled, label: 'SMS'),
SwitchField(value: receivePush, onChanged: handlePushToggled, label: '푸시 알림'),
// 이런 경우 CheckboxGroup이 더 적합
],
)
다수 항목의 선택/해제를 관리할 때는 Checkbox나 CheckboxGroup이 더 적합하다.
✅ Do#
설정 즉시 적용되는 스위치에 로딩 상태 처리
SwitchField(
value: isFeatureEnabled,
onChanged: (value) async {
unawaited(settingsService.updateFeature(value));
handleFeatureToggled(value);
},
label: '베타 기능 사용',
enabled: !isLoading, // API 호출 중 비활성화
)
비동기 설정 변경 중 중복 클릭을 방지하여 일관된 상태를 유지한다.
❌ Don't#
부정적 표현의 라벨 사용
// ❌ 부정 표현은 ON/OFF 상태와 혼란을 야기함
SwitchField(
value: isNotificationsDisabled, // 부정 상태를 저장
onChanged: handleNotificationsDisabledToggled,
label: '알림 끄기', // ON이 "알림 끄기"? 혼란스러움
)
스위치 라벨은 항상 긍정형으로 작성해야 ON 상태의 의미가 명확하다.
✅ Do#
레이블을 항상 Switch 옆에 배치하세요.
SwitchField(
label: '마케팅 알림 수신',
description: '이벤트 및 프로모션 알림을 받습니다.',
value: marketingEnabled,
onChanged: handleMarketingToggled,
)
레이블 없는 스위치는 무엇을 켜고 끄는지 알 수 없습니다. 레이블과 선택적 설명을 함께 제공하여 명확한 맥락을 만들어야 합니다.
❌ Don't#
Switch로 즉각 실행되는 위험한 동작을 제어하지 마세요.
// ❌ Switch 토글 즉시 계정 삭제 시작
SwitchField(
label: '계정 비활성화',
value: isDeactivated,
onChanged: (value) {
if (value) deactivateAccountImmediately(); // 위험한 즉시 실행
},
)
Switch는 즉각적인 ON/OFF 상태 변경에 적합합니다. 계정 삭제, 데이터 초기화 등 위험한 동작은 Dialog로 확인을 받으세요.
접근성 (Accessibility)#
키보드 인터랙션#
| 키 | 동작 |
|---|---|
Space | 스위치 상태 토글 |
Enter | 스위치 상태 토글 |
Tab | 다음 포커스 가능 요소로 이동 |
Shift+Tab | 이전 포커스 가능 요소로 이동 |
스크린 리더#
-
Flutter:
Semantics로switchrole,label,checked상태 전달 (예: "다크 모드, 스위치, 켜짐") -
Web:
role="switch",aria-checked,aria-label(label 텍스트),aria-describedby(description) 자동 적용
터치 타겟#
-
최소 터치 타겟 크기: 24×24 (WCAG 2.2
2.5.8).CoreTouchTarget.minimum이 단일 출처이고,TouchTarget(Flutter) /coui-touch-target(Web)이 그리는 크기는 그대로 둔 채 닿는 범위만 넓힙니다. 플랫폼 가이드는 더 큰 값(iOS 44 · Android 48)을 권장하며, 컴포넌트가 그보다 크게 그리는 것은 자유입니다 — 24 는 그 아래로 내려가면 틀린 선입니다. - 스위치 + 레이블 전체 영역이 터치 가능 영역
크로스 플랫폼 차이점 (Platform Differences)#
named properties 표면(label · description · value · onChanged
· enabled · switchFieldStyle)은 양 플랫폼이 동일합니다. 아래는 렌더 경로 차이입니다.
| 항목 | Flutter | Web |
|---|---|---|
| 렌더링 | Clickable + Row + Toggle |
Clickable + <div> + Toggle |
| chrome 적용 | resolver 가 굽는 Flutter 값 | resolver 가 내보내는 class / inline CSS |
| 필수성 | value / onChanged 가 required |
둘 다 optional (기본값 있음) |
관련 컴포넌트 (Related Components)#
- Toggle: 라벨 없이 단독으로 사용하는 최소한의 스위치
- Checkbox: 폼에서 항목 선택 여부를 관리하는 경우
- RadioGroup: 여러 옵션 중 하나를 배타적으로 선택하는 경우
조합 예제#
// SwitchField 목록 + Fieldset 조합: 알림 설정 섹션
Fieldset(
legend: Text('알림 설정').titleSmall.onSurface,
children: [
SwitchField(
value: pushEnabled,
onChanged: handlePushToggled,
label: '푸시 알림',
description: '새 메시지와 업데이트를 알려드립니다',
),
SwitchField(
value: emailEnabled,
onChanged: handleEmailToggled,
label: '이메일 알림',
description: '주간 요약 및 중요 공지를 이메일로 받습니다',
),
SwitchField(
value: marketingEnabled,
onChanged: handleMarketingToggled,
label: '마케팅 알림',
description: '할인 혜택 및 이벤트 정보를 받습니다',
),
],
)