material_drum_picker 1.5.0
material_drum_picker: ^1.5.0 copied to clipboard
A Material 3 date picker with an iOS-style drum roller, full API parity with showDatePicker and CupertinoDatePicker, and drum, calendar and input modes.
material_drum_picker #
A Material Design 3 date, time, and date+time picker with an iOS style drum
roller. It offers full API parity with Flutter's showDatePicker and
CupertinoDatePicker, uses Material 3 color tokens, and ships three context
aware date modes (drum, calendar, and keyboard input).
Showcase #
Every look below is rendered straight from the package: the drum mode (with day of week), the calendar mode (quick selects plus disabled weekends), the keyboard input mode, a combined date and time picker, and a dark theme.

Run the live demo for every option with cd example && flutter run, then open
the Showcase screen.
Features #
- Drum mode. An iOS style scroll wheel. Ideal for birth dates and expiry dates.
- Calendar mode. A Material 3 calendar grid with year navigation and configurable quick select chips.
- Input mode. A keyboard text field with live
MM/DD/YYYYvalidation. - Date and time. Opt in with
pickTime: true(orshowDrumDateTimePicker) to add an hour and minute drum, plus an AM/PM column in 12 hour mode. - Time only. Use
DrumTimePickerorshowDrumTimePickerto pick just aTimeOfDay, configurable for AM/PM or 24 hour mode. - Hijri (Umm al-Qura) calendar. Set
calendar: DrumCalendarType.hijrito show every date mode in the lunar Hijri calendar, composed with the existing right to left layout. The returned value stays a GregorianDateTime. - Pluggable data backed calendars. Drive the picker from a published
dataset of month start dates (for an official or committee lunar calendar)
with
TabularLunarCalendarSystem, passed throughcalendarSystem. - Full API parity with
showDatePickerandCupertinoDatePicker. Shared parameters keep the same names so migration is a one line change. selectableDayPredicateto disable weekends, holidays, or any custom rule, enforced in all three date modes.quickSelectOptionsfor custom chips such as Today, Next Monday, or +3 Days.columnOrderfor Day/Month/Year, Month/Day/Year, or Year/Month/Day.monthFormatto show the drum month as a name or a number, andinputFormatto control the typed date layout (for exampleDrumDateFormat.parse('DD-MM-YYYY')).- Material 3 theming through
ColorSchemetokens, with per app or per picker overrides via theDrumPickerThemeextension. - Right to left support for Arabic, Hebrew, and Persian, with the weekday and column order flipping automatically.
- Accessibility: 44dp touch targets, keyboard navigation in the calendar, screen reader semantics, and reduced motion support.
- All six platforms: Android, iOS, web, macOS, Windows, and Linux.
- Zero runtime dependencies beyond Flutter and
intl.
Pickers at a glance #
| Function | Returns | Use it for |
|---|---|---|
showDrumDatePicker |
DateTime? |
A date |
showDrumDateTimePicker |
DateTime? |
A date and a time |
showDrumTimePicker |
TimeOfDay? |
A time only |
Each function has an inline widget equivalent (DrumPicker and
DrumTimePicker) for embedding in a form without a dialog.
Date and time #

Time only #

Hijri (Umm al-Qura) #
Show every date mode in the lunar Hijri calendar. The value you receive is still
a Gregorian DateTime.
final picked = await showDrumDatePicker(
context: context,
firstDate: DateTime(2000),
lastDate: DateTime(2050),
calendar: DrumCalendarType.hijri,
locale: const Locale('ar'),
);
The built in lunar calendar is Umm al-Qura, the official civil calendar of Saudi Arabia. The Persian solar (Jalali) calendar is a separate system and is not included; the abstraction is designed so it can be added later.

The same picker in Arabic flips right to left, shows Arabic month names and
digits, and starts the week on Saturday, exactly the way the Gregorian calendar
localizes. Set showGregorianAlongside: true to show the Gregorian equivalent
under the headline.
Data backed calendars #
Some official and committee lunar calendars are published as data, year by year,
and cannot be computed from a formula. Drive the picker from a dataset of month
start dates with TabularLunarCalendarSystem, passed through calendarSystem,
which takes precedence over calendar.
// Provide your own dataset of contiguous Hijri month starts, plus a trailing
// sentinel entry (the start of the month after the last selectable month).
final system = TabularLunarCalendarSystem.fromJsonList(jsonDecode(assetString));
final picked = await showDrumDatePicker(
context: context,
firstDate: system.minSupported,
lastDate: system.maxSupported,
calendarSystem: system,
locale: const Locale('ar'),
);
Calendars such as Taqweem al-Hadi (the Bahraini Ja'fari calendar) are expressed
this way. The package ships only the mechanism and the documented schema, never
any specific publisher's data. Supply the dataset from your own app, with the
publisher's permission and attribution, and refresh it roughly once a Hijri
year. Compare your lastDate with system.maxSupported to detect that the data
is near its end.
Installation #
dependencies:
material_drum_picker: ^1.2.0
Add flutter_localizations to your app if you have not already:
dependencies:
flutter_localizations:
sdk: flutter
Then register the delegates in your MaterialApp:
MaterialApp(
localizationsDelegates: GlobalMaterialLocalizations.delegates,
supportedLocales: const [Locale('en'), /* your locales */],
)
Quick start #
A date (drop in replacement for showDatePicker) #
import 'package:material_drum_picker/material_drum_picker.dart';
final DateTime? picked = await showDrumDatePicker(
context: context,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
);
A time only #
final TimeOfDay? time = await showDrumTimePicker(
context: context,
initialTime: TimeOfDay.now(),
use24hFormat: true, // null follows MediaQuery.alwaysUse24HourFormat
minuteInterval: 5, // 0, 5, 10, ...
);
Inline, embedded in a form:
DrumTimePicker(
initialTime: const TimeOfDay(hour: 9, minute: 0),
use24hFormat: false, // shows an AM/PM column
minuteInterval: 15,
showActions: false,
onChanged: (time) => setState(() => _time = time),
)
A date and time #
final DateTime? when = await showDrumDateTimePicker(
context: context,
firstDate: DateTime(2020),
lastDate: DateTime(2030),
use24hFormat: true,
minuteInterval: 15,
);
// `when` carries the chosen hour and minute.
Birth date picker #
final today = DateTime.now();
final birthDate = await showDrumDatePicker(
context: context,
initialMode: DrumPickerMode.drum, // a wheel for distant dates
firstDate: DateTime(today.year - 120),
lastDate: DateTime(today.year - 18, today.month, today.day),
columnOrder: DrumColumnOrder.dmy, // Day, Month, Year
showModeToggle: false, // lock to drum mode
helpText: 'SELECT BIRTH DATE',
);
Appointment picker without weekends #
final appointment = await showDrumDatePicker(
context: context,
initialMode: DrumPickerMode.calendar,
firstDate: DateTime.now(),
lastDate: DateTime.now().add(const Duration(days: 90)),
selectableDayPredicate: (day) =>
day.weekday != DateTime.saturday && day.weekday != DateTime.sunday,
confirmText: 'BOOK APPOINTMENT',
);
Custom quick selects #
showDrumDatePicker(
context: context,
firstDate: DateTime.now().add(const Duration(days: 1)),
lastDate: DateTime.now().add(const Duration(days: 30)),
quickSelectOptions: [
DrumQuickSelect.relative(label: 'Express +1', offset: const Duration(days: 1)),
DrumQuickSelect.relative(label: 'Standard +3', offset: const Duration(days: 3)),
DrumQuickSelect.relative(label: 'Economy +7', offset: const Duration(days: 7)),
],
);
API reference #
showDrumDatePicker and showDrumDateTimePicker #
| Parameter | Type | Default | Description |
|---|---|---|---|
context |
BuildContext |
required | Build context |
firstDate |
DateTime |
required | Minimum selectable date |
lastDate |
DateTime |
required | Maximum selectable date |
initialDate |
DateTime? |
today | Pre selected date |
currentDate |
DateTime? |
DateTime.now() |
The "today" marker |
selectableDayPredicate |
SelectableDayPredicate? |
null | Return false to disable a day |
initialMode |
DrumPickerMode |
.drum |
Starting date mode |
showModeToggle |
bool |
true |
Show the mode tabs |
columnOrder |
DrumColumnOrder? |
locale default | Column order in drum mode |
monthFormat |
DrumMonthFormat |
.name |
Drum month as a name or a number |
inputFormat |
DrumDateFormat |
.mdy |
Typed input order, separator, year width |
showDayOfWeekInDrum |
bool |
false |
Show weekday in the drum day column |
showQuickSelects |
bool |
true |
Show quick select chips |
quickSelectOptions |
List<DrumQuickSelect>? |
Today/Tomorrow/+7d | Custom chips |
pickTime |
bool |
false |
Also pick a time of day |
use24hFormat |
bool? |
ambient | 24 hour time strip (no AM/PM) |
minuteInterval |
int |
1 |
Minute granularity (a divisor of 60) |
helpText |
String? |
'SELECT DATE' |
Header label |
confirmText |
String? |
'OK' |
Confirm button text |
cancelText |
String? |
'Cancel' |
Cancel button text |
errorFormatText |
String? |
'Invalid format' |
Input mode format error |
errorInvalidText |
String? |
'Out of range' |
Input mode range error |
fieldHintText |
String? |
'MM/DD/YYYY' |
Input field hint |
fieldLabelText |
String? |
'Enter Date' |
Input field label |
locale |
Locale? |
ambient | Locale override |
textDirection |
TextDirection? |
ambient | Text direction override |
barrierDismissible |
bool |
true |
Tap outside to dismiss |
barrierColor |
Color? |
Colors.black54 |
Barrier color |
barrierLabel |
String? |
localized | Barrier accessibility label |
useRootNavigator |
bool |
true |
Use the root navigator |
routeSettings |
RouteSettings? |
null | Route settings |
restorationId |
String? |
null | State restoration id |
anchorPoint |
Offset? |
null | Split screen anchor |
builder |
TransitionBuilder? |
null | Wrap the dialog with a Theme, and so on |
showDrumDateTimePicker takes the same parameters and is simply
showDrumDatePicker with pickTime set to true.
showDrumTimePicker #
| Parameter | Type | Default | Description |
|---|---|---|---|
context |
BuildContext |
required | Build context |
initialTime |
TimeOfDay? |
now | Pre selected time |
use24hFormat |
bool? |
ambient | 24 hour mode (no AM/PM column) |
minuteInterval |
int |
1 |
Minute granularity (a divisor of 60) |
helpText |
String? |
'SELECT TIME' |
Header label |
confirmText |
String? |
'OK' |
Confirm button text |
cancelText |
String? |
'Cancel' |
Cancel button text |
locale |
Locale? |
ambient | Locale override |
textDirection |
TextDirection? |
ambient | Text direction override |
barrierDismissible |
bool |
true |
Tap outside to dismiss |
barrierColor |
Color? |
Colors.black54 |
Barrier color |
barrierLabel |
String? |
null | Barrier accessibility label |
useRootNavigator |
bool |
true |
Use the root navigator |
routeSettings |
RouteSettings? |
null | Route settings |
anchorPoint |
Offset? |
null | Split screen anchor |
builder |
TransitionBuilder? |
null | Wrap the dialog with a Theme, and so on |
Inline widgets #
DrumPicker accepts every showDrumDatePicker parameter, plus the callbacks
onChanged, onConfirmed, onCancelled, and onModeChanged, and the
showActions flag. DrumTimePicker accepts every showDrumTimePicker
parameter, plus onChanged, onConfirmed, onCancelled, and showActions.
Set showActions: false to drop the built in Cancel and OK buttons and drive
the value yourself with onChanged.
DrumColumnOrder #
| Value | Format | Typical regions |
|---|---|---|
dmy |
15 Jun 2024 | UK, Europe, MENA, Australia |
mdy |
Jun 15 2024 | United States, Canada |
ymd |
2024 Jun 15 | Japan, China, Korea |
ydm |
2024 15 Jun | Rarely used |
DrumPickerMode #
| Value | Best for |
|---|---|
drum |
Birth dates, expiry dates, distant past or future |
calendar |
Scheduling events, appointments, near future |
input |
Power users, accessibility tools, typed entry |
DrumPickerTheme #
DrumPickerTheme exposes the picker's colors, typography, shape, and spacing.
Apply it app wide through ThemeData.extensions, or to a single picker through
the theme parameter. A per instance theme is merged over the ambient
extension, which is merged over Material 3 defaults derived from your
ColorScheme, so you only set what you want to change.
// App wide:
ThemeData(
useMaterial3: true,
extensions: const [
DrumPickerTheme(
headerBackgroundColor: Color(0xFF004D40),
headerTextColor: Colors.white,
itemExtent: 48,
visibleItemCount: 3,
),
],
)
// Just one picker, without touching the app theme:
DrumPicker(
firstDate: DateTime(2000),
lastDate: DateTime(2100),
theme: const DrumPickerTheme(
// Calendar grid selection (previously not themeable).
selectedDayBackgroundColor: Colors.deepPurple,
selectedDayForegroundColor: Colors.white,
todayColor: Colors.deepPurple,
// Square-ish day cells instead of circles.
dayShape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
),
// Typography tokens merge over the defaults, so this only changes weight.
headlineTextStyle: TextStyle(fontWeight: FontWeight.w700),
selectorBandRadius: 16,
),
)
Available tokens: headerBackgroundColor, headerTextColor,
cardBackgroundColor, selectorBandColor, selectedItemColor,
unselectedItemColor, dayForegroundColor, selectedDayBackgroundColor,
selectedDayForegroundColor, todayColor, disabledDayColor, helpTextStyle,
headlineTextStyle, secondaryTextStyle, columnLabelTextStyle,
selectedItemTextStyle, unselectedItemTextStyle, dayShape,
selectorBandRadius, headerPadding, itemExtent, and visibleItemCount.
DrumPickerLabels #
The drum column headers (DAY, MONTH, YEAR), the time strip headers (HOUR, MIN,
AM/PM), the mode toggle (Calendar, Drum, Input), and the default quick select
chips are fixed strings. Pass DrumPickerLabels to translate or relabel them:
DrumPicker(
firstDate: DateTime(2000),
lastDate: DateTime(2100),
labels: const DrumPickerLabels(
dayColumn: 'JOUR',
monthColumn: 'MOIS',
yearColumn: 'ANNEE',
calendarMode: 'Calendrier',
drumMode: 'Molette',
inputMode: 'Saisie',
),
)
Custom input field decoration #
Pass inputDecoration so the input mode field matches your form styling (for
example a filled field), instead of the default outlined border:
DrumPicker(
firstDate: DateTime(2000),
lastDate: DateTime(2100),
inputDecoration: const InputDecoration(filled: true),
)
Month as a name or a number, and a custom typed format #

Use monthFormat to show the drum month column as a name (default) or as a
zero padded number. It works with every calendar system and locale.
DrumPicker(
firstDate: DateTime(2000),
lastDate: DateTime(2100),
monthFormat: DrumMonthFormat.numeric, // 06 instead of Jun
)
Use inputFormat to control how the keyboard input mode lays out a date: the
order of the day, month, and year fields, the separator, and whether the year is
two or four digits. Build one from a pattern, or use a preset
(DrumDateFormat.mdy, .dmy, .ymd):
DrumPicker(
firstDate: DateTime(2000),
lastDate: DateTime(2100),
inputFormat: DrumDateFormat.parse('DD-MM-YYYY'),
// Other examples:
// DrumDateFormat.parse('YYYY.MM.DD')
// DrumDateFormat.parse('DD/MM/YY') // two digit year
// DrumDateFormat.ymd // ISO style YYYY-MM-DD
)
The format drives how the field shows the current value, the hint, and how it
parses what the user types, in whatever calendar the picker is using. A two
digit year is resolved into the supported range at the year nearest the current
selection. For the drum wheel column order, use columnOrder.
Localization #
The pickers follow the ambient locale for month and weekday names, the first
day of the week, AM/PM labels, the column order, and right to left layout. Pass
locale and textDirection to override them for a single picker. The time
format follows MediaQuery.alwaysUse24HourFormat unless you set use24hFormat.
The calendar system is independent of locale and direction. You can combine any
calendar with any locale: for example Umm al-Qura with en shows Latin digits
and English Hijri month names, while Umm al-Qura with an Arabic locale shows the
Arabic month names and that locale's digits, flipping right to left exactly the
way the Gregorian calendar does.
Migration from showDatePicker #
Most parameters keep the same name, so usually only the function name changes:
// Before
showDatePicker(
context: context,
initialDate: myDate,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
selectableDayPredicate: myPredicate,
helpText: 'PICK DATE',
locale: myLocale,
);
// After, identical parameter names
showDrumDatePicker(
context: context,
initialDate: myDate,
firstDate: DateTime(1900),
lastDate: DateTime(2100),
selectableDayPredicate: myPredicate,
helpText: 'PICK DATE',
locale: myLocale,
initialMode: DrumPickerMode.calendar, // optional, same feel as showDatePicker
);
Contributing #
Contributions are welcome. Please read CONTRIBUTING.md for the development setup and the checks that run in CI, and note the Code of Conduct. To report a security issue, see SECURITY.md.
Roadmap #
- v1.0 Single date picker (drum, calendar, and input modes).
- v1.1 Combined date and time picking (
pickTime,showDrumDateTimePicker). - v1.2 Standalone time picker (
DrumTimePicker,showDrumTimePicker). - v1.3 Hijri (Umm al-Qura) calendar and pluggable data backed calendars.
- Next Date range selection (
showDrumDateRangePicker), and a Persian solar (Jalali) calendar system.
License #
MIT, 2026. See LICENSE.