Calendar#
날짜를 시각적으로 탐색하고 선택할 수 있는 캘린더 컴포넌트입니다.
Live Preview#
class CalendarNoneExample extends StatefulComponent {
const CalendarNoneExample({super.key});
@override
State<CalendarNoneExample> createState() => _CalendarNoneExampleState();
}
class _CalendarNoneExampleState extends State<CalendarNoneExample> {
@override
Component build(BuildContext context) {
return Calendar(
selectionMode: CoreCalendarSelectionMode.none,
);
}
}
class CalendarNoneExample extends StatefulWidget {
const CalendarNoneExample({super.key});
@override
State<CalendarNoneExample> createState() => _CalendarNoneExampleState();
}
class _CalendarNoneExampleState extends State<CalendarNoneExample> {
@override
Widget build(BuildContext context) {
return Calendar(
selectionMode: CoreCalendarSelectionMode.none,
);
}
}
class CalendarDefaultExample extends StatefulComponent {
const CalendarDefaultExample({super.key});
@override
State<CalendarDefaultExample> createState() => _CalendarDefaultExampleState();
}
class _CalendarDefaultExampleState extends State<CalendarDefaultExample> {
@override
Component build(BuildContext context) {
return Calendar.single();
}
}
class CalendarDefaultExample extends StatefulWidget {
const CalendarDefaultExample({super.key});
@override
State<CalendarDefaultExample> createState() => _CalendarDefaultExampleState();
}
class _CalendarDefaultExampleState extends State<CalendarDefaultExample> {
@override
Widget build(BuildContext context) {
return Calendar.single();
}
}
class CalendarMultiExample extends StatefulComponent {
const CalendarMultiExample({super.key});
@override
State<CalendarMultiExample> createState() => _CalendarMultiExampleState();
}
class _CalendarMultiExampleState extends State<CalendarMultiExample> {
@override
Component build(BuildContext context) {
return Calendar.multi();
}
}
class CalendarMultiExample extends StatefulWidget {
const CalendarMultiExample({super.key});
@override
State<CalendarMultiExample> createState() => _CalendarMultiExampleState();
}
class _CalendarMultiExampleState extends State<CalendarMultiExample> {
@override
Widget build(BuildContext context) {
return Calendar.multi();
}
}
class CalendarRangeExample extends StatefulComponent {
const CalendarRangeExample({super.key});
@override
State<CalendarRangeExample> createState() => _CalendarRangeExampleState();
}
class _CalendarRangeExampleState extends State<CalendarRangeExample> {
@override
Component build(BuildContext context) {
return Calendar.range();
}
}
class CalendarRangeExample extends StatefulWidget {
const CalendarRangeExample({super.key});
@override
State<CalendarRangeExample> createState() => _CalendarRangeExampleState();
}
class _CalendarRangeExampleState extends State<CalendarRangeExample> {
@override
Widget build(BuildContext context) {
return Calendar.range();
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 날짜를 시각적으로 탐색하고 선택해야 할 때
- 범위(체크인/체크아웃) 또는 다중 날짜 선택이 필요할 때
대신 다른 컴포넌트를 사용하세요:
DatePicker: 입력 필드와 결합된 날짜 선택 폼 요소일 때TextField: 단순 날짜 텍스트 입력만 필요할 때
기본 사용법 (Basic Usage)#
// 단일 선택
Calendar.single(
onChanged: (value) => print('Selected: $value'),
)
// 범위 선택
Calendar.range(
onChanged: (value) => print('Range: $value'),
)
// 다중 선택
Calendar.multi(
onChanged: (value) => print('Multi: $value'),
)
// 단일 선택
Calendar.single(
onChanged: (value) => print('Selected: $value'),
)
// 범위 선택
Calendar.range(
onChanged: (value) => print('Range: $value'),
)
// 다중 선택
Calendar.multi(
onChanged: (value) => print('Multi: $value'),
)
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
selectionMode |
CoreCalendarSelectionMode |
single |
선택 모드 |
value |
CoreCalendarValue? |
null |
현재 선택 값 |
onChanged |
Function(CoreCalendarValue?)? |
null |
선택 변경 콜백 |
initialView |
CoreCalendarView? |
null |
초기 표시 월/년 |
stateBuilder |
CoreCalendarDateStateBuilder? |
null |
날짜별 활성/비활성 결정 |
minDate |
DateTime? |
null |
최소 선택 가능 날짜 |
maxDate |
DateTime? |
null |
최대 선택 가능 날짜 |
calendarStyle |
CoreCalendarStyle? |
null |
인스턴스별 스타일 (chrome / 슬롯 / 상태 토큰) |
스타일 시스템 (Style System)#
Calendar 의 모든 chrome / dimensional / 슬롯 / 상태 토큰 override 는
단일 calendarStyle: CoreCalendarStyle 슬롯으로 흐릅니다 (Epic #1302).
selectionMode / initialView 같은 시맨틱 enum 과 동작 필드는 위젯에 그대로
남고, 스타일 슬롯에는 들어가지 않습니다 (원칙 6 / 7).
구조#
CoreCalendarStyle 은 chrome 슬롯 + nested sub-component Style + state-token
색상 슬롯을 단일 진입점으로 보유합니다.
| 필드 | 타입 | default | 적용 대상 |
|---|---|---|---|
dayCellSize |
double? |
defaultDayCellSize (space32) |
일 / 월 / 연도 셀 크기 |
monthCellWidth |
double? |
defaultMonthCellWidth (size56) |
월 / 연도 셀 너비 |
cellSpacing |
double? |
defaultCellSpacing (space8) |
셀 간 간격 |
headerGridGapStyle |
CoreGapStyle? |
defaultHeaderGridGapStyle (CoreGapStyle(size: space16)) |
헤더 ↔ 그리드 간격 (Gap 슬롯) |
yearRowSpacing |
double? |
defaultYearRowSpacing (space8) |
연도 그리드 행 간격 |
arrowIconSize |
double? |
defaultArrowIconSize (space12) |
화살표 아이콘 크기 |
endpointPillBorderRadius |
CoreBorderRadius? |
defaultEndpointPillBorderRadius (radius8) |
range 양 끝 pill 모서리 |
fadedOpacity |
double? |
defaultFadedOpacity (opacity50) |
다른 달 날짜 불투명도 |
yearGridSize |
int? |
defaultYearGridSize (16) |
연도 그리드 총 항목 수 |
monthGridColumns |
int? |
defaultMonthGridColumns (4) |
월 그리드 열 수 |
yearGridColumns |
int? |
defaultYearGridColumns (4) |
연도 그리드 열 수 |
navButtonStyle |
CoreButtonStyle? |
— | prev / next 네비 버튼 / 헤더 버튼 |
dateCellStyle |
CoreButtonStyle? |
— | 날짜 / 월 / 연도 셀 버튼 chrome |
arrowIconStyle |
CoreIconStyle? |
— | prev / next 화살표 아이콘 |
headerTextStyle |
CoreTextStyle? |
defaultHeaderTextStyle (bodySmall) |
헤더 라벨 ("May 2026") |
weekdayTextStyle |
CoreTextStyle? |
defaultWeekdayTextStyle (labelMedium) |
요일 라벨 (Sun / Mon …) |
cellTextStyle |
CoreTextStyle? |
defaultCellTextStyle (bodySmall) |
날짜 / 월 / 연도 셀 텍스트 |
selectedColor |
CoreColor? |
defaultSelectedColor (primary) |
선택된 셀 배경 |
selectedTextColor |
CoreColor? |
defaultSelectedTextColor (onPrimary) |
선택된 셀 텍스트 |
todayColor | CoreColor? | — | 오늘 셀 배경 |
todayTextColor | CoreColor? | — | 오늘 셀 텍스트 |
rangeSelectionColor |
CoreColor? |
defaultRangeSelectionColor (secondary) |
range 내부 셀 배경 |
rangeSelectionTextColor |
CoreColor? |
defaultRangeSelectionTextColor (onSecondary) |
range 내부 셀 텍스트 |
weekdayLabelColor |
CoreColor? |
defaultWeekdayLabelColor (onSurface) |
요일 라벨 색 |
상태 토큰들은 grid 안의 state-coloured 셀로, 단일 sub-component chrome 에
귀속되지 않으므로 nested slot Style 이 아닌 평면(flat) CoreColor 슬롯으로 노출합니다.
Resolve chain#
design system default
→ CoreCalendarTheme.style // 프로젝트 공통
→ parent component slot override
(예: CoreDatePickerStyle.calendarStyle)
→ widget.calendarStyle // 인스턴스별
CoreCalendarStyle.merge 는 nested slot Style (navButtonStyle 등) 을
재귀적으로 머지하므로 부모 / theme / 인스턴스 단계에서 부분 override 가
가능합니다.
사용 예#
Calendar(
selectionMode: CoreCalendarSelectionMode.single,
calendarStyle: CoreCalendarStyle(
selectedColor: CoreColor.token(CoreColors.success),
dateCellStyle: CoreButtonStyle(
labelStyle: CoreTextStyle(fontWeight: CoreFontWeight.semiBold),
),
),
onChanged: (value) => print(value),
)
Calendar(
selectionMode: CoreCalendarSelectionMode.single,
calendarStyle: CoreCalendarStyle(
selectedColor: CoreColor.token(CoreColors.primary),
rangeSelectionColor: CoreColor.token(CoreColors.secondary),
),
onChanged: (value) => print(value),
)
변형 (Variants)#
단일 선택 (Single)#
Calendar.single(
onChanged: (value) => print('Selected: $value'),
)
범위 선택 (Range)#
Calendar.range(
onChanged: (value) => print('Range: $value'),
)
범위 모드에서는 듀얼 캘린더 레이아웃이 표시됩니다.
다중 선택 (Multi)#
Calendar.multi(
onChanged: (value) => print('Multi: $value'),
)
표시 전용 (None)#
Calendar(
selectionMode: CoreCalendarSelectionMode.none,
)
동작 스펙 (Behavior)#
선택 모드#
CoreCalendarSelectionMode.none // 표시 전용
CoreCalendarSelectionMode.single // 단일 날짜 선택
CoreCalendarSelectionMode.range // 시작~끝 범위 선택
CoreCalendarSelectionMode.multi // 다중 날짜 선택
- Single: 클릭으로 선택, 다시 클릭하면 해제
- Range: 첫 번째 클릭=시작, 두 번째 클릭=끝. 자동 정렬(start < end)
- Multi: 클릭할 때마다 선택/해제 토글
CoreCalendarValue#
// 단일 선택
CoreCalendarValue.single(DateTime(2024, 3, 15))
// 범위 선택
CoreCalendarValue.range(DateTime(2024, 3, 10), DateTime(2024, 3, 20))
// 다중 선택
CoreCalendarValue.multi([DateTime(2024, 3, 5), DateTime(2024, 3, 15)])
뷰 전환#
CoreCalendarViewType.date: 월별 일 그리드 (6행 × 7열)CoreCalendarViewType.month: 월 선택 (4×3 그리드)CoreCalendarViewType.year: 연도 선택 (4×4 그리드)- 헤더의 월/연도를 클릭하면 상위 뷰로 전환
날짜 상태 검증#
Calendar.single(
stateBuilder: (date) {
if (date.isBefore(DateTime.now())) return CoreCalendarDateState.disabled;
return CoreCalendarDateState.enabled;
},
onChanged: (value) => print(value),
)
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | Calendar | Calendar |
| 값 모델 | CoreCalendarValue | CoreCalendarValue |
| 선택 모드 | none/single/range/multi | none/single/range/multi |
| 뷰 전환 | date → month → year | date → month → year |
| 범위 레이아웃 | 듀얼 캘린더 | 듀얼 캘린더 |
| 네비게이션 | Button(variant: .outline) |
Button(variant: .outline) |
| 날짜 셀 | Button(variant: .ghost/.primary/.secondary) |
Button(variant: .ghost/.primary/.secondary) |
관련 컴포넌트 (Related Components)#
- DatePicker: 입력 필드 + 캘린더 팝업