Skip to content

Pickers

Date, time, and date-range pickers all share a common structure: an editable / read-only text trigger field with an inline popover anchored below. All values flow through as UTC DateTime instances.

Live Example

Open the full route: Pickers.

DateTimePicker

Editable text field + popover. Drives mode (date / time / dateTime) to choose what the popover shows. The text field enforces the format via DateTimeInputFormatter so users can either type or pick.

dart
DateTimePicker(
  mode: DateTimePickerMode.dateTime,
  value: selected,
  onChanged: (dt) => setState(() => selected = dt),  // dt is UTC
  firstDate: DateTime(2020),
  lastDate: DateTime(2030),
  minuteStep: 5,
  datePattern: 'MM/DD/YYYY',  // default
  timePattern: 'HH:mm',       // default
  showToday: true,            // default — show Today quick-jump in date modes
)

DateTimePickerMode

ModeTrigger patternPopover
dateMM/DD/YYYYCalendar only
timeHH:mmTimeScrollPicker only
dateTimeMM/DD/YYYY HH:mmCalendar + time scroll side-by-side

CdxDateRangePicker

Range-specific variant. Read-only trigger displays MMM d, y - MMM d, y (formatted via FieldFormatter.instance.formatDate). The popover is a single-month calendar with shadcn-style range visuals: filled circles on start/end, light band between.

dart
CdxDateRangePicker(
  value: range,                 // DateTimeRange?
  firstDate: DateTime(2020),
  lastDate: DateTime(2030),
  onChanged: (r) => setState(() => range = r),
  hintText: 'Pick a date range',
)

Two-tap selection model:

  • First tap → start, end cleared
  • Second tap → end (swapped if before start)
  • Third tap → resets start

DateTimePopover

The popover content used by DateTimePicker (date / time / dateTime modes). Useful when you want to embed the picker UI without the text trigger.

dart
DateTimePopover(
  value: current,
  showDate: true,
  showTime: true,
  onChanged: (dt) => store.setSchedule(dt),
  firstDate: DateTime(2020),
  lastDate: DateTime(2030),
  minuteStep: 15,
  showToday: true,
  onTodaySelected: () => analytics.track('today_selected'),
)

Calendar popovers

Two raw calendar popovers underlie the dateTime / range pickers:

WidgetSelectionUse directly
CdxSingleDatePopoverSingle date — single tap commits, optional Today action via showToday / onTodayChangedCustom date pickers
CdxDateRangePopoverTwo-tap range, with mid-range band visualCustom range pickers

Both share the same header (tappable month-year title opens a year grid) and weekday/day grid aesthetic.

TimeScrollPicker

iOS-style two-column scrollable time picker. Each column auto-centers the selection.

dart
TimeScrollPicker(
  hour: time.hour,
  minute: time.minute,
  minuteStep: 15,                              // snaps minutes to nearest valid step
  onHourChanged: (h) => store.setHour(h),
  onMinuteChanged: (m) => store.setMinute(m),
)

Fixed at 120 px wide, expands to fill its parent's vertical constraint via Expanded.

TimeStepper

Compact ◀ 22 ▶ row with arrow buttons and a tappable middle that enters direct-edit mode. Mouse scroll wheel increments / decrements. Values wrap past min / max.

dart
TimeStepper(
  value: minutes,
  min: 0,
  max: 59,
  step: 1,
  onChanged: (v) => store.setMinutes(v),
)

DateTimeInputFormatter

TextInputFormatter enforcing a date/time pattern. Auto-inserts separators (/, -, ., :, ), validates field ranges inline (e.g. month can only start with 0 or 1), rejects impossible dates (Feb 30), and exposes tryParse / format helpers.

TokenMeaning
DDDay (01–31)
MMMonth (01–12)
YYYYYear (4 digits)
HHHour (00–23)
mmMinute (00–59)
dart
final formatter = DateTimeInputFormatter(pattern: 'YYYY-MM-DD HH:mm');

TextField(
  inputFormatters: [formatter],
  keyboardType: TextInputType.number,
);

final dt = formatter.tryParse('2026-04-18 14:30');  // local DateTime or null
final str = formatter.format(DateTime.now());        // '2026-04-18 14:30'

UTC contract

Every picker emits UTC values via onChanged, and accepts UTC values as value. Internal display logic converts to local for the calendar / text trigger so users see their local date/time, but the round-trip is always UTC. Match this in your store: store UTC, display local through the picker.

  • OverlaysbuildOverlayFollower positioning
  • Inputs — text-input formatting patterns
  • Field FormattingFieldFormatter for displaying formatted dates/times outside the picker