Slider#
연속적인 값 또는 범위를 선택할 수 있는 슬라이더 컴포넌트입니다.
Live Preview#
Web
Flutter
Loading Flutter...
class SliderDefaultExample extends StatefulComponent {
const SliderDefaultExample({super.key});
@override
State<SliderDefaultExample> createState() => _SliderDefaultExampleState();
}
class _SliderDefaultExampleState extends State<SliderDefaultExample> {
double _value = 50;
@override
Component build(BuildContext context) {
return CoSlider(
value: _value,
min: 0,
max: 100,
onChanged: (v) => setState(() => _value = v),
);
}
}
class SliderDefaultExample extends StatefulWidget {
const SliderDefaultExample({super.key});
@override
State<SliderDefaultExample> createState() => _SliderDefaultExampleState();
}
class _SliderDefaultExampleState extends State<SliderDefaultExample> {
double _value = 50;
@override
Widget build(BuildContext context) {
return CoSlider(
value: _value,
min: 0,
max: 100,
onChanged: (v) => setState(() => _value = v),
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 볼륨, 밝기 등 연속적인 값을 직관적으로 조절할 때
- 가격 범위, 나이 범위 등 두 값 사이의 범위를 선택할 때
- 값의 상대적 위치를 시각적으로 확인하면서 조절할 때
대신 다른 컴포넌트를 사용하세요:
Input: 정확한 숫자 입력이 필요할 때Progress: 사용자 조작 없이 진행률만 표시할 때Select: 불연속적인 옵션 중 선택할 때
기본 사용법 (Basic Usage)#
// 기본 슬라이더
CoSlider(
value: volume,
onChanged: handleVolumeChange,
min: 0,
max: 100,
)
// 단계 지정
CoSlider(
value: rating,
onChanged: handleRatingChange,
min: 0,
max: 5,
divisions: 5,
)
// 비활성화
CoSlider(
value: volume,
enabled: false,
min: 0,
max: 100,
)
// 기본 슬라이더
CoSlider(
value: volume,
onChanged: handleVolumeChange,
min: 0,
max: 100,
)
// 단계(step) 지정
CoSlider(
value: rating,
onChanged: handleRatingChange,
min: 0,
max: 5,
step: 1,
)
// 비활성화
CoSlider(
value: volume,
enabled: false,
min: 0,
max: 100,
)
스타일 시스템 — sliderStyle (Epic #1302)#
CoSlider 의 track + thumb chrome / 슬롯 미세 조정은 단일 sliderStyle
(CoreSliderStyle) 으로 흐릅니다. Slider 는 시맨틱 enum (variant) 이 없어
behaviour 필드만 위젯 파라미터.
CoSlider(
value: temperature,
min: 0,
max: 100,
divisions: 10,
onChanged: handleChange,
sliderStyle: CoreSliderStyle(
trackColor: cs.outline,
activeColor: cs.primary,
trackHeight: 4,
thumbColor: cs.onPrimary,
thumbBorderColor: cs.outline,
thumbSize: 20,
tickLabelStyle: CoreTextStyle(fontSize: 12, color: cs.onSurfaceVariant),
),
)
CoreSliderStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
trackColor | Color? / String? | 비활성 트랙 색 |
activeColor |
Color? / String? |
활성 (filled) 트랙 색 |
trackHeight | double? | 트랙 높이 |
thumbColor | Color? / String? | thumb 배경 |
thumbBorderColor |
Color? / String? |
thumb 보더 |
thumbSize | double? | thumb 크기 |
tickLabelStyle |
CoreTextStyle? |
tick 레이블 (divisions 사용 시) |
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
value | double | 0.0 | 현재 값 |
onChanged |
ValueChanged<double>? |
null |
값 변경 콜백 |
onChangeStart / onChangeEnd |
ValueChanged<double>? |
null |
드래그 시작/종료 |
min | double | 0.0 | 최솟값 |
max | double | 100.0 | 최댓값 |
step | double? | null | 단계 크기 |
divisions | int? | null | 단계 수 |
enabled | bool | true | 활성화 여부 |
sliderStyle |
CoreSliderStyle? |
null |
track + thumb chrome / nested tickLabelStyle 묶음 |
변형 (Variants)#
단계 지정#
CoSlider(
value: temperature,
onChanged: handleTempChange,
min: 16,
max: 30,
divisions: 14,
)
Step 지정#
CoSlider(
value: rating,
onChanged: handleRatingChange,
min: 0,
max: 5,
step: 1,
)
동작 스펙 (Behavior)#
SliderController#
CoSlider(
value: 50,
min: 0,
max: 100,
onChanged: (v) => setState(() => _value = v),
)
value,min,max,onChanged기본 APIstep또는divisions으로 이산 값 스냅
드래그 인터랙션#
- 단일 모드: 트랙 아무 곳이나 클릭하면 썸이 이동. 썸 드래그 지원
- 범위 모드: 클릭 시 가까운 썸이 선택됨. 각 썸 독립 드래그
- 드래그 중
onChangeStart/onChangeEnd콜백 호출
Divisions (스냅)#
CoSlider(
value: rating,
onChanged: handleRating,
min: 0, max: 5, divisions: 5,
)
divisions가 설정되면 값이 가장 가까운 단계에 스냅SliderValue.roundToDivisions(int)메서드로 반올림- 시각적 틱 마크 표시
Hint Value#
CoSlider(
value: volume,
onChanged: handleVolume,
min: 0,
max: 100,
)
내부 상수#
| 상수 | 값 | 설명 |
|---|---|---|
| 트랙 높이 | 6px | 기본 트랙 두께 |
| 썸 크기 | 16px | 드래그 핸들 크기 |
| 비활성 알파 | 0.2 | 트랙 비활성 부분 불투명도 |
| 테두리 알파 | 0.5 | 썸 테두리 불투명도 |
키보드 네비게이션#
increaseStep/decreaseStep으로 화살표 키 단계 크기 지정FocusableActionDetector기반 포커스 관리
Web 구현#
- 네이티브 HTML
<input type="range">오버레이 (opacity-0) - 커스텀 트랙/range div로 시각적 표현
step속성으로 단계 크기 지정showValue: true로 현재 값 라벨 표시
사용 가이드라인 (Usage Guidelines)#
✅ Do#
슬라이더에 현재 값을 라벨로 표시하세요.
CoSlider(
value: temperature,
onChanged: handleTemp,
min: 16, max: 30,
divisions: 14,
)
사용자가 정확한 값을 확인하면서 조절할 수 있습니다.
❌ Don't#
정밀한 값 입력에 슬라이더만 사용하지 마세요.
// ❌ Bad — 정확한 금액 입력이 어려움
CoSlider(value: price, min: 0, max: 1000000, onChanged: handlePrice)
// ✅ Good — 슬라이더 + 입력 필드 조합
Row(children: [
Expanded(child: CoSlider(value: price, min: 0, max: 1000000, onChanged: handlePrice)),
SizedBox(width: 16),
SizedBox(width: 80, child: CoTextField(initialValue: price.toString())),
])
✅ Do#
범위 슬라이더로 필터를 구현하세요.
CoSlider(
value: minPrice,
onChanged: handleMinPriceFilter,
min: 0,
max: 500000,
step: 10000,
)
CoSlider(
value: maxPrice,
onChanged: handleMaxPriceFilter,
min: 0,
max: 500000,
step: 10000,
)
가격, 나이 등 범위 필터에 직관적입니다.
❌ Don't#
3개 이상의 값이 필요하면 슬라이더를 사용하지 마세요.
슬라이더는 1~2개 값(단일/범위)에 최적화되어 있습니다. 복잡한 입력은 개별 필드를 사용하세요.
✅ Do#
이산 값에는 divisions를 설정하세요.
// 별점 1~5
CoSlider(
value: stars, min: 1, max: 5, divisions: 4,
onChanged: handleStars,
)
연속 값이 아닌 경우 스냅으로 정확한 선택을 보장합니다.
접근성 (Accessibility)#
키보드 인터랙션#
| 키 | 동작 |
|---|---|
← / ↓ | 값 감소 (decreaseStep 만큼) |
→ / ↑ | 값 증가 (increaseStep 만큼) |
Home | 최솟값으로 이동 |
End | 최댓값으로 이동 |
Tab | 다음 요소로 포커스 이동 |
스크린 리더#
- Flutter: 현재 값, 최솟값, 최댓값이 시맨틱으로 전달
-
Web: 네이티브
<input type="range">의aria-valuenow,aria-valuemin,aria-valuemax자동 적용
터치 영역#
- 썸(16px)보다 넓은 터치 영역 확보 (트랙 전체 클릭 가능)
- 모바일에서 충분한 드래그 감도
크로스 플랫폼 차이점 (Platform Differences)#
v3.0부터 기본 API (
enabled,onChanged,value등)가 통일되었습니다. 아래는 플랫폼 고유 차이점만 나열합니다.
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | CoSlider | CoSlider |
| 값 모델 | double | double |
| Divisions | divisions (int) | divisions (int) |
| Step | step (double) | step (double) |
| 트랙 렌더링 | 커스텀 GestureDetector + 페인팅 |
<input type="range"> + CSS 오버레이 |
| 키보드 | Arrow keys 커스텀 | 브라우저 네이티브 |
| 색상 커스텀 | activeColor: Color | activeColor: CoreColor |
| 테마 | Flutter Theme 기반 | Tailwind CSS |
관련 컴포넌트 (Related Components)#
조합 예제#
// 가격 범위 필터 패턴
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('가격 범위'),
SizedBox(height: 8),
CoSlider(
value: minPrice,
onChanged: (v) => setState(() => minPrice = v),
min: 0,
max: 500000,
step: 10000,
),
CoSlider(
value: maxPrice,
onChanged: (v) => setState(() => maxPrice = v),
min: 0,
max: 500000,
step: 10000,
),
SizedBox(height: 4),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('₩${minPrice.toInt()}'),
Text('₩${maxPrice.toInt()}'),
],
),
],
)