TimePicker#
시간(시·분·초)을 선택할 수 있는 시간 선택기 컴포넌트입니다. 12시간/24시간 형식과 선택적인 초 표시를 지원합니다.
Live Preview#
기본 (popover)#
class TimePickerDefaultExample extends StatefulComponent {
const TimePickerDefaultExample({super.key});
@override
State<TimePickerDefaultExample> createState() =>
_TimePickerDefaultExampleState();
}
class _TimePickerDefaultExampleState extends State<TimePickerDefaultExample> {
String? _value;
@override
Component build(BuildContext context) {
return div(
[
CoTimePicker(
value: _value,
placeholder: 'Select time',
onChanged: (value) => setState(() => _value = value),
),
],
classes: 'w-${CoreSpace.scale.space256}',
);
}
}
class TimePickerDefaultExample extends StatefulWidget {
const TimePickerDefaultExample({super.key});
@override
State<TimePickerDefaultExample> createState() =>
_TimePickerDefaultExampleState();
}
class _TimePickerDefaultExampleState extends State<TimePickerDefaultExample> {
String? _value;
@override
Widget build(BuildContext context) {
return SizedBox(
width: CoreSpace.space256,
child: CoTimePicker(
value: _value,
placeholder: 'Select time',
onChanged: (value) => setState(() => _value = value),
),
);
}
}
다이얼로그 모드#
class TimePickerDialogExample extends StatefulComponent {
const TimePickerDialogExample({super.key});
@override
State<TimePickerDialogExample> createState() =>
_TimePickerDialogExampleState();
}
class _TimePickerDialogExampleState extends State<TimePickerDialogExample> {
String? _value;
@override
Component build(BuildContext context) {
return div(
[
CoTimePicker(
value: _value,
placeholder: 'Select time',
mode: CorePromptMode.dialog,
dialogTitle: Text('Pick a time'),
onChanged: (value) => setState(() => _value = value),
),
],
classes: 'w-${CoreSpace.scale.space256}',
);
}
}
class TimePickerDialogExample extends StatefulWidget {
const TimePickerDialogExample({super.key});
@override
State<TimePickerDialogExample> createState() =>
_TimePickerDialogExampleState();
}
class _TimePickerDialogExampleState extends State<TimePickerDialogExample> {
String? _value;
@override
Widget build(BuildContext context) {
return SizedBox(
width: CoreSpace.space256,
child: CoTimePicker(
value: _value,
placeholder: 'Select time',
mode: CorePromptMode.dialog,
dialogTitle: const Text('Pick a time'),
onChanged: (value) => setState(() => _value = value),
),
);
}
}
초 단위 (HH:MM:SS)#
class TimePickerSecondsExample extends StatefulComponent {
const TimePickerSecondsExample({super.key});
@override
State<TimePickerSecondsExample> createState() =>
_TimePickerSecondsExampleState();
}
class _TimePickerSecondsExampleState extends State<TimePickerSecondsExample> {
String? _value;
@override
Component build(BuildContext context) {
return div(
[
CoTimePicker(
value: _value,
placeholder: 'Select time',
showSeconds: true,
onChanged: (value) => setState(() => _value = value),
),
],
classes: 'w-${CoreSpace.scale.space256}',
);
}
}
class TimePickerSecondsExample extends StatefulWidget {
const TimePickerSecondsExample({super.key});
@override
State<TimePickerSecondsExample> createState() =>
_TimePickerSecondsExampleState();
}
class _TimePickerSecondsExampleState extends State<TimePickerSecondsExample> {
String? _value;
@override
Widget build(BuildContext context) {
return SizedBox(
width: CoreSpace.space256,
child: CoTimePicker(
value: _value,
placeholder: 'Select time',
showSeconds: true,
onChanged: (value) => setState(() => _value = value),
),
);
}
}
에러 상태#
class TimePickerErrorExample extends StatefulComponent {
const TimePickerErrorExample({super.key});
@override
State<TimePickerErrorExample> createState() =>
_TimePickerErrorExampleState();
}
class _TimePickerErrorExampleState extends State<TimePickerErrorExample> {
String? _value;
@override
Component build(BuildContext context) {
return div(
[
CoTimePicker(
value: _value,
placeholder: 'Select time',
errorText: 'Time is required',
onChanged: (value) => setState(() => _value = value),
),
],
classes: 'w-${CoreSpace.scale.space256}',
);
}
}
class TimePickerErrorExample extends StatefulWidget {
const TimePickerErrorExample({super.key});
@override
State<TimePickerErrorExample> createState() =>
_TimePickerErrorExampleState();
}
class _TimePickerErrorExampleState extends State<TimePickerErrorExample> {
String? _value;
@override
Widget build(BuildContext context) {
return SizedBox(
width: CoreSpace.space256,
child: CoTimePicker(
value: _value,
placeholder: 'Select time',
errorText: 'Time is required',
onChanged: (value) => setState(() => _value = value),
),
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 예약, 일정, 알림 등 시간을 입력받아야 하는 경우
- 12시간(AM/PM) 또는 24시간 형식의 시간 선택이 필요한 경우
- 영업 시간, 배송 시간처럼 선택 가능한 시간 범위를 제한해야 하는 경우
대신 다른 컴포넌트를 사용하세요:
DatePicker: 날짜와 시간을 함께 선택해야 하는 경우Input: 시간을 자유 형식 텍스트로 직접 입력받는 경우Select: 고정된 시간 슬롯 목록에서 선택해야 하는 경우
기본 사용법 (Basic Usage)#
// 기본 시간 선택기
CoTimePicker(
value: selectedTime,
placeholder: 'Select time',
onChanged: handleTimeChanged,
)
// 24시간 형식 강제
CoTimePicker(
value: meetingTime,
use24HourFormat: true,
onChanged: handleMeetingTimeChanged,
)
// 초 표시 포함
CoTimePicker(
value: preciseTime,
showSeconds: true,
onChanged: handlePreciseTimeChanged,
)
// 선택 가능 범위 제한 (ISO time string)
CoTimePicker(
value: businessHour,
min: '09:00',
max: '18:00',
onChanged: handleBusinessHourChanged,
)
// 기본 시간 선택기
CoTimePicker(
value: selectedTime,
placeholder: 'Select time',
onChanged: handleTimeChanged,
)
// 24시간 형식 강제
CoTimePicker(
value: meetingTime,
use24HourFormat: true,
onChanged: handleMeetingTimeChanged,
)
// 초 표시 포함
CoTimePicker(
value: preciseTime,
showSeconds: true,
onChanged: handlePreciseTimeChanged,
)
// 선택 가능 범위 제한
CoTimePicker(
value: businessHour,
min: '09:00',
max: '18:00',
onChanged: handleBusinessHourChanged,
)
Props / Parameters#
공통 Contract 필드 (CoreTimePickerContract):
| 속성 | Flutter | Web | 기본값 | 설명 |
|---|---|---|---|---|
value |
String? |
String? |
null |
현재 선택된 시간 (ISO 형식: HH:MM 또는 HH:MM:SS) |
placeholder |
String? |
String? |
자동 (--:--) / 'Select time' |
미선택 시 표시될 텍스트 |
enabled |
bool |
bool |
true |
활성화 여부 |
showSeconds |
bool |
bool |
false |
초 선택 포함 여부 |
use24HourFormat |
bool? |
bool? |
null (플랫폼 기본값) |
24시간 형식 사용 여부 |
min |
String? |
String? |
null |
최소 선택 가능 시간 (ISO 문자열) |
max |
String? |
String? |
null |
최대 선택 가능 시간 (ISO 문자열) |
onChanged |
ValueChanged<String>? |
CoreValueChanged<String>? |
null |
시간 변경 콜백 (ISO 문자열) |
mode |
CorePromptMode? |
CorePromptMode? |
CorePromptMode.popover |
트리거 클릭 시 popover/dialog 중 어떤 surface로 열릴지 |
dialogTitle |
Widget? |
Component? |
null |
mode = dialog일 때 dialog 헤더에 표시되는 타이틀 |
errorText |
String? |
String? |
null |
에러 메시지(있으면 보더가 error 색으로 표시됨, CoTextField와 패리티) |
timePickerStyle |
CoreTimePickerStyle<Color, List<BoxShadow>>? |
CoreTimePickerStyle<String, String>? |
null |
인스턴스 스타일 (Style 시스템 참조) |
Web 확장 필드:
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
id | String? | null | DOM 요소 id |
classes |
String? |
null |
트리거에 적용할 추가 CSS 클래스 |
css |
Styles? |
null |
루트에 적용할 인라인 스타일 |
스타일 시스템 (Style System)#
TimePicker의 모든 chrome / dimensional / nested-slot 오버라이드는 CoreTimePickerStyle<Clr, Shadow>
단일 슬롯으로 흐릅니다 (Epic #1302 원칙 6/7/8). 평면 chrome 필드(borderRadius / padding /
height / gap / iconSize / popoverPadding / popoverGap)는 모두 제거되었습니다.
시맨틱 vs 스타일#
-
시맨틱 enum / behaviour: 위젯 파라미터로 직접 (
mode,placeholder,enabled,showSeconds,use24HourFormat,onChanged,min,max,errorText,dialogTitle) -
chrome / dimensional / 슬롯 스타일:
CoreTimePickerStyle한곳으로 (popoverStyle/dialogStyle/segmentInputStyle/amPmButtonStyle/labelStyle/descriptionStyle/errorStyle) - 변형(variant) 교체: asChild —
dialogTitle슬롯 등에 직접 위젯 주입
Resolve chain#
design system default for time picker
→ CoreTimePickerTheme.style // 프로젝트 공통
→ parent component slot override
→ widget.timePickerStyle // 인스턴스별
각 nested 슬롯 스타일 (popoverStyle / dialogStyle / segmentInputStyle
/ amPmButtonStyle) 은 자기 컴포넌트의 자체 resolve chain 으로 다시 한 번 머지됩니다. 예: popoverStyle.triggerStyle
의 chrome 은 → CoreButtonTheme → variant 룩업 → 부모 슬롯 → widget.timePickerStyle.popoverStyle.triggerStyle
순.
옛 평면 필드 → 새 위치 매핑#
| 옛 chrome 필드 | 새 위치 |
|---|---|
height | popoverStyle.triggerStyle.height |
padding | popoverStyle.triggerStyle.paddingH |
borderRadius | popoverStyle.triggerStyle.borderRadius |
gap (트리거 내부) | popoverStyle.triggerStyle.gap |
iconSize | popoverStyle.triggerStyle.trailingIconStyle.size |
popoverPadding | popoverStyle.panelPadding |
popoverGap | popoverStyle.gap |
사용 예 (Flutter)#
CoTimePicker(
value: '14:30',
placeholder: 'Select time',
onChanged: handleTimeChanged,
timePickerStyle: CoreTimePickerStyle(
popoverStyle: CorePopoverStyle(
panelBorderRadius: 12,
panelPadding: 16,
gap: 8,
triggerStyle: CoreButtonStyle(
height: 44,
paddingH: 16,
),
),
segmentInputStyle: CoreTextFieldStyle(
borderColor: Color(0xFF3B82F6),
),
amPmButtonStyle: CoreButtonStyle(
height: 36,
),
labelStyle: CoreTextStyle(fontWeight: 600),
),
)
동작 스펙 (Behavior)#
인터랙션#
- 트리거 클릭: 양쪽 모두
CoPopover패널이 트리거 아래로 열립니다 - 직접 입력: 시/분/초 필드에 숫자를 직접 입력 가능 (양쪽 동일)
- AM/PM 토글: 12시간 형식에서 AM/PM 전환 —
CoButton기반 (양쪽 동일) - 저장/취소: 패널 하단의
CoButton(Save/Cancel)
값 형식#
- 값은 ISO 시간 문자열로 주고받습니다:
"14:30"또는"14:30:45"(showSeconds 시) - 12시간 포맷에서도 값은 24시간 ISO로 저장되며, 디스플레이만
hh:mm AM/PM로 포맷팅됩니다
Theme 오버라이드#
CoreComponentTheme.timePicker를 통해 프로젝트 레벨의 기본 스타일(CoreTimePickerStyle)을 지정할 수 있습니다:
CoreComponentTheme(
timePicker: CoreTimePickerTheme(
style: CoreTimePickerStyle(
popoverStyle: CorePopoverStyle(
panelBorderRadius: 8,
panelPadding: 16,
triggerStyle: CoreButtonStyle(height: 40, paddingH: 12),
),
),
),
)
접근성 (Accessibility)#
- 트리거에
role="button"+aria-haspopup="dialog"적용 - 패널에
role="dialog"적용 - 키보드로 시/분/초 입력 가능 (양쪽 동일)
- 터치 타겟 최소 크기: 40dp (field 기본 높이)
크로스 플랫폼 차이점 (Platform Differences)#
양쪽 모두 CoPopover + CoButton + CoIcon을 사용해 동일한 UX를 제공합니다. 값 형식, 토큰, 동작 모두 1:1 통일되어 있습니다.
관련 컴포넌트 (Related Components)#
- DatePicker: 날짜와 시간을 함께 선택해야 하는 경우
- Input: 시간을 텍스트로 직접 입력받는 경우
- Select: 고정된 시간 슬롯 목록을 드롭다운으로 제공하는 경우