WFormDatePicker
A form-integrated date picker that wraps WDatePicker with Flutter's FormField<DateTime>. Provides native form validation, automatic error: state styling, and optional label, hint, and error message display.
On this page
forms/form_date_picker_basic
A live demo of this example follows.
form_date_picker_basic.dart
View source
Open demo
WFormDatePicker(
label: 'Birth Date',
hint: 'We need your date of birth for verification',
className: 'p-3 border border-gray-300 rounded-lg error:border-red-500',
validator: (value) => value == null ? 'Please select a date' : null,
onChanged: (date) => print('Selected: $date'),
)
Basic Usage#
WFormDatePicker works inside a Flutter Form widget. It renders a vertical layout with an optional label above the date picker and error/hint text below.
final _formKey = GlobalKey<FormState>();
Form(
key: _formKey,
child: Column(
children: [
WFormDatePicker(
label: 'Event Date',
className: 'p-3 border rounded-lg error:border-red-500',
validator: (date) => date == null ? 'Date is required' : null,
onChanged: (date) => _eventDate = date,
),
WButton(
className: 'mt-4 bg-blue-500 text-white px-4 py-2 rounded-lg',
onTap: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
}
},
child: WText('Submit'),
),
],
),
)
When validation fails, the widget automatically adds the error state to the inner WDatePicker, activating any error: prefixed classes.
Constructor#
WFormDatePicker({
Key? key,
// FormField params
FormFieldValidator<DateTime>? validator,
FormFieldSetter<DateTime>? onSaved,
AutovalidateMode? autovalidateMode,
bool enabled = true,
// WDatePicker params
DateTime? initialValue,
DateRange? initialRange,
WDatePickerMode mode = WDatePickerMode.single,
ValueChanged<DateTime>? onChanged,
ValueChanged<DateRange>? onRangeChanged,
DateTime? minDate,
DateTime? maxDate,
String? className,
String? placeholder,
Set<String>? states,
DateDisplayFormat? displayFormat,
// dateTime mode params, forwarded to WDatePicker
int minuteStep = 5,
String timeLabel = 'Time',
String doneLabel = 'Done',
// Form wrapper params
String? label,
String labelClassName = 'text-sm font-medium text-gray-700 dark:text-gray-300 mb-1',
bool showError = true,
String errorClassName = 'text-red-500 dark:text-red-400 text-xs mt-1',
String? hint,
String hintClassName = 'text-gray-500 dark:text-gray-400 text-xs mt-1',
})
Props#
FormField Props#
| Prop | Type | Default | Description |
|---|---|---|---|
validator |
FormFieldValidator<DateTime>? |
null |
Validation function, receives DateTime? |
onSaved |
FormFieldSetter<DateTime>? |
null |
Called when form is saved |
autovalidateMode |
AutovalidateMode? |
null |
When to auto-validate |
enabled |
bool |
true |
Whether the picker is interactive |
initialValue |
DateTime? |
null |
Initial selected instant (single and dateTime modes; carries the time of day in dateTime). Seeds the field's state on first build only, as FormField does: changing it later does not move an already-picked value |
Date Picker Props#
| Prop | Type | Default | Description |
|---|---|---|---|
mode |
WDatePickerMode |
single |
Selection mode: single, range or dateTime |
initialRange |
DateRange? |
null |
Initial date range (range mode) |
onChanged |
ValueChanged<DateTime>? |
null |
Called on selection (single and dateTime modes) |
onRangeChanged |
ValueChanged<DateRange>? |
null |
Called on range selection (range mode) |
minDate |
DateTime? |
null |
Earliest selectable date |
maxDate |
DateTime? |
null |
Latest selectable date |
className |
String? |
null |
Wind utility classes for the trigger |
placeholder |
String? |
null |
Placeholder text (defaults to 'Select date') |
states |
Set<String>? |
null |
Custom states for dynamic styling |
displayFormat |
DateDisplayFormat? |
null |
Custom date display format function |
minuteStep |
int |
5 |
Minutes each step control moves, forwarded to the picker (dateTime mode only) |
timeLabel |
String |
'Time' |
Label above the time row, forwarded to the picker (dateTime mode only) |
doneLabel |
String |
'Done' |
Confirm control text that closes the popover, forwarded to the picker (dateTime mode only) |
Layout Props#
| Prop | Type | Default | Description |
|---|---|---|---|
label |
String? |
null |
Label text displayed above the picker |
labelClassName |
String |
'text-sm font-medium text-gray-700 dark:text-gray-300 mb-1' |
Wind classes for the label |
showError |
bool |
true |
Whether to display validation error text |
errorClassName |
String |
'text-red-500 dark:text-red-400 text-xs mt-1' |
Wind classes for error message |
hint |
String? |
null |
Hint text displayed below the picker |
hintClassName |
String |
'text-gray-500 dark:text-gray-400 text-xs mt-1' |
Wind classes for hint text |
Form Validation#
The validator function receives the currently selected DateTime? value. Return a String to show an error, or null if valid.
WFormDatePicker(
label: 'Departure Date',
className: 'p-3 border rounded-lg error:border-red-500 error:bg-red-50',
validator: (date) {
if (date == null) return 'Please select a departure date';
if (date.isBefore(DateTime.now())) return 'Date must be in the future';
return null;
},
)
Auto-Validation#
Use autovalidateMode to validate as the user interacts:
WFormDatePicker(
label: 'Start Date',
autovalidateMode: AutovalidateMode.onUserInteraction,
className: 'p-3 border rounded-lg error:border-red-500',
validator: (date) => date == null ? 'Required' : null,
)
Using onSaved#
DateTime? _savedDate;
WFormDatePicker(
label: 'Meeting Date',
className: 'p-3 border rounded-lg',
onSaved: (date) => _savedDate = date,
validator: (date) => date == null ? 'Required' : null,
)
Range Mode in Forms#
In range mode, the FormField<DateTime> internally tracks the range's start date for validation purposes. This means a simple null check still works as a "required" validator.
forms/form_date_picker_range
A live demo of this example follows.
form_date_picker_range.dart
View source
Open demo
WFormDatePicker(
label: 'Trip Dates',
mode: WDatePickerMode.range,
initialRange: null,
className: 'p-3 border rounded-lg error:border-red-500',
placeholder: 'Select check-in / check-out',
validator: (date) => date == null ? 'Please select your trip dates' : null,
onRangeChanged: (range) {
if (range.isComplete) {
_calculateTripDuration(range);
}
},
)
Date + Time in Forms#
With mode: WDatePickerMode.dateTime the field holds a full DateTime carrying the picked hour and minute, so a validator can compare times of day rather than just checking for null. minuteStep, timeLabel, and doneLabel are forwarded to WDatePicker unchanged.
widgets/date_picker_datetime
A live demo of this example follows.
date_picker_datetime.dart
View source
Open demo
WFormDatePicker(
mode: WDatePickerMode.dateTime,
label: 'Call with the customer',
hint: 'Office hours run 09:00 to 18:00',
minuteStep: 15,
doneLabel: 'Confirm',
className: 'p-3 border rounded-lg error:border-red-500',
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value) {
if (value == null) return 'Pick a date and a time';
if (value.hour < 9 || value.hour >= 18) {
return 'Office hours run 09:00 to 18:00';
}
return null;
},
)
Two behaviours carry over from the raw widget. Once a day is picked, every time step updates the field value, not only the day tap, because onChanged fires on each one; the validator runs on those changes when autovalidateMode is always or onUserInteraction (as above), when an enclosing Form sets its own mode, and on validate(); AutovalidateMode.onUnfocus validates on focus loss instead, not on each change; and initialValue seeds the field on first build only, the way FormField does, so changing it later does not move an already-picked value.
Event Handling#
onChanged and onRangeChanged fire alongside the form state updates. The FormFieldState is updated automatically; you don't need to call didChange yourself.
WFormDatePicker(
label: 'Appointment',
onChanged: (date) {
// FormField state is already updated at this point
_loadAvailableSlots(date);
},
)
State Variants#
WFormDatePicker inherits all state variants from WDatePicker and adds the error state automatically when validation fails.
| State | Activated When |
|---|---|
hover: |
Mouse hovers over the trigger |
focus: |
Calendar popover is open |
open: |
Calendar popover is open (alias) |
disabled: |
enabled is false |
selected: |
A date or range has been selected |
error: |
Form validation has failed |
WFormDatePicker(
label: 'Date',
className: 'p-3 border border-gray-300 rounded-lg '
'hover:border-blue-400 '
'focus:border-blue-500 focus:ring-2 focus:ring-blue-200 '
'error:border-red-500 error:ring-2 error:ring-red-200 '
'disabled:opacity-50 disabled:bg-gray-100',
validator: (date) => date == null ? 'Required' : null,
)
Styling Examples#
Standard Form Field#
WFormDatePicker(
label: 'Date of Birth',
hint: 'You must be at least 18 years old',
className: 'w-full p-3 border border-gray-300 rounded-lg '
'focus:border-blue-500 focus:ring-2 focus:ring-blue-200 '
'error:border-red-500',
maxDate: DateTime.now().subtract(const Duration(days: 365 * 18)),
validator: (date) => date == null ? 'Date of birth is required' : null,
)
Dark Mode Ready#
WFormDatePicker(
label: 'Schedule',
labelClassName: 'text-sm font-medium text-gray-700 dark:text-gray-300',
className: 'p-3 bg-white dark:bg-gray-800 '
'border border-gray-300 dark:border-gray-600 rounded-lg '
'hover:border-blue-400 dark:hover:border-blue-500 '
'error:border-red-500 dark:error:border-red-400',
errorClassName: 'text-red-500 dark:text-red-400 text-xs mt-1',
validator: (date) => date == null ? 'Required' : null,
)
Minimalist#
WFormDatePicker(
label: 'When',
labelClassName: 'text-xs text-gray-500 uppercase tracking-wider',
className: 'border-b border-gray-200 py-2 rounded-none '
'focus:border-blue-500 error:border-red-500',
hintClassName: 'text-gray-400 text-xs mt-0.5',
hint: 'Optional',
)
All Supported Classes#
Trigger (className)#
All Wind utility classes work on the trigger container. The most commonly used:
| Category | Examples |
|---|---|
| Background | bg-white, bg-gray-50, dark:bg-gray-800 |
| Border | border, border-gray-300, rounded-lg |
| Padding | p-3, px-4, py-2 |
| Sizing | w-full, w-64 |
| Ring | ring-2, ring-blue-200 |
| Shadow | shadow-sm |
| State prefixes | hover:, focus:, disabled:, selected:, error:, dark: |
Label (labelClassName)#
Default: text-sm font-medium text-gray-700 dark:text-gray-300 mb-1
Error (errorClassName)#
Default: text-red-500 dark:text-red-400 text-xs mt-1
Hint (hintClassName)#
Default: text-gray-500 dark:text-gray-400 text-xs mt-1
Customizing Theme#
WFormDatePicker inherits all theme customisation from WDatePicker. Override Tailwind colour scales in WindThemeData to change selection colours, error states, and calendar chrome. The colours you pass merge over the defaults by name: a name you supply replaces that whole palette, so list every shade you use.
WindTheme(
data: WindThemeData(
colors: {
'red': MaterialColor(0xFFEF4444, {
200: Color(0xFFFECACA), // Error ring color
400: Color(0xFFF87171), // Dark mode error text (errorClassName)
500: Color(0xFFEF4444), // Error border and error text color
}),
},
),
child: MyApp(),
)
Related Documentation#
- WDatePicker - The base date picker widget
- WFormInput - Form-integrated text input
- WFormSelect - Form-integrated dropdown
- WFormCheckbox - Form-integrated checkbox
- WPopover - The underlying overlay engine