DatePickerAppi
A comprehensive date picker widget with custom styling, validation, and flexible display modes for Flutter applications.
Features
- Multiple Display Modes: Popup dialog or custom overlay
- Date Range Validation: Configurable first and last date limits
- Form Integration: Full form validation support
- Focus Management: Automatic focus handling and navigation
- Custom Styling: Inherits from TextFieldAppi styling
- Keyboard Support: Proper keyboard navigation and handling
- Accessibility: Screen reader support and semantic labels
- Flexible Date Ranges: Support for relative date ranges (e.g., years before current)
Usage
Basic Date Picker
DatePickerAppi(
textFieldStyle: TextFieldParamsAppi(
hint: 'Select Date',
widgetKey: GlobalKey<FormFieldState<String>>(),
),
onChange: (dateString) {
print('Selected date: $dateString');
},
)
Date Picker with Validation
DatePickerAppi(
textFieldStyle: TextFieldParamsAppi(
hint: 'Birth Date',
widgetKey: _birthDateKey,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please select your birth date';
}
return null;
},
mandatory: true,
),
onChange: (dateString) {
setState(() => _selectedBirthDate = dateString);
},
firstDate: DateTime(1900),
lastDate: DateTime.now(),
)
Date Picker with Custom Range
DatePickerAppi(
textFieldStyle: TextFieldParamsAppi(
hint: 'Appointment Date',
widgetKey: _appointmentDateKey,
heading: 'Select Appointment Date',
),
onChange: (dateString) {
_scheduleAppointment(dateString);
},
firstDate: DateTime.now(),
lastDate: DateTime.now().add(Duration(days: 365)),
initialDate: DateTime.now().add(Duration(days: 1)),
)
Date Picker with Years Before Current
DatePickerAppi(
textFieldStyle: TextFieldParamsAppi(
hint: 'Experience Start Date',
widgetKey: _experienceDateKey,
),
onChange: (dateString) {
_updateExperience(dateString);
},
beforeYear: 10, // Shows dates from 10 years ago
popup: true,
)
Parameters
Name | Type | Description | Default |
---|---|---|---|
textFieldStyle | TextFieldParamsAppi | Required. Text field configuration and styling | - |
onChange | Function(String) | Required. Callback when date is selected | - |
initialDate | DateTime? | Initial date to show in picker | Current date or calculated from beforeYear |
firstDate | DateTime? | Earliest selectable date | DateTime(1900) |
lastDate | DateTime? | Latest selectable date | DateTime(2100) |
popup | bool | Whether to show as popup dialog | true |
nextFocusNode | FocusNode? | Focus node to move to after selection | null |
beforeYear | int? | Years before current year for initial date | null |
TextFieldParamsAppi Integration
The DatePickerAppi uses TextFieldParamsAppi
for all text field related configurations:
TextFieldParamsAppi(
// Basic properties
hint: 'Select Date',
heading: 'Date of Birth',
mandatory: true,
// Validation
validator: (value) => value?.isEmpty == true ? 'Required' : null,
widgetKey: GlobalKey<FormFieldState<String>>(),
// Styling
border: OutlineInputBorder(),
fillColor: Colors.white,
textStyle: TextStyle(fontSize: 16),
// Focus management
focus: FocusNode(),
)
Best Practices
- Date Ranges: Always set appropriate
firstDate
andlastDate
for your use case - Validation: Provide clear validation messages for required date fields
- Initial Values: Set sensible initial dates based on context
- Focus Management: Use
nextFocusNode
for smooth form navigation - Accessibility: Provide meaningful hints and labels
- Error Handling: Handle edge cases like invalid date ranges
- User Experience: Use popup mode for better mobile experience
Examples
Birth Date Picker
DatePickerAppi(
textFieldStyle: TextFieldParamsAppi(
hint: 'DD/MM/YYYY',
heading: 'Date of Birth',
mandatory: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Birth date is required';
}
// Additional age validation
final selectedDate = DateTime.tryParse(value);
if (selectedDate != null) {
final age = DateTime.now().difference(selectedDate).inDays ~/ 365;
if (age < 18) {
return 'Must be at least 18 years old';
}
}
return null;
},
widgetKey: _birthDateKey,
),
onChange: (dateString) {
setState(() => _birthDate = dateString);
},
firstDate: DateTime(1900),
lastDate: DateTime.now().subtract(Duration(days: 365 * 18)), // 18 years ago
)
Event Date Picker
DatePickerAppi(
textFieldStyle: TextFieldParamsAppi(
hint: 'Select Event Date',
heading: 'Event Date',
prefixIcon: Icons.event,
widgetKey: _eventDateKey,
),
onChange: (dateString) {
_updateEventDate(dateString);
},
firstDate: DateTime.now(),
lastDate: DateTime.now().add(Duration(days: 365 * 2)),
initialDate: DateTime.now().add(Duration(days: 7)),
)
Form Integration
Form(
key: _formKey,
child: Column(
children: [
DatePickerAppi(
textFieldStyle: TextFieldParamsAppi(
hint: 'Start Date',
heading: 'Project Start Date',
mandatory: true,
validator: (value) => value?.isEmpty == true ? 'Start date required' : null,
widgetKey: _startDateKey,
focus: _startDateFocus,
),
onChange: (dateString) {
setState(() => _startDate = dateString);
_validateDateRange();
},
nextFocusNode: _endDateFocus,
),
SizedBox(height: 16),
DatePickerAppi(
textFieldStyle: TextFieldParamsAppi(
hint: 'End Date',
heading: 'Project End Date',
mandatory: true,
validator: (value) {
if (value?.isEmpty == true) return 'End date required';
if (_startDate.isNotEmpty && value != null) {
final start = DateTime.tryParse(_startDate);
final end = DateTime.tryParse(value);
if (start != null && end != null && end.isBefore(start)) {
return 'End date must be after start date';
}
}
return null;
},
widgetKey: _endDateKey,
focus: _endDateFocus,
),
onChange: (dateString) {
setState(() => _endDate = dateString);
_validateDateRange();
},
firstDate: _startDate.isNotEmpty ? DateTime.tryParse(_startDate) : DateTime.now(),
),
SizedBox(height: 24),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_submitForm();
}
},
child: Text('Submit'),
),
],
),
)
Custom Styling
DatePickerAppi(
textFieldStyle: TextFieldParamsAppi(
hint: 'Select Date',
heading: 'Custom Styled Date',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide(color: Colors.blue),
),
fillColor: Colors.blue.shade50,
textStyle: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
prefixIcon: Icons.calendar_today,
widgetKey: _customDateKey,
),
onChange: (dateString) {
_handleCustomDate(dateString);
},
)
See Also
- TextFieldAppi - For text field styling and validation
- BottomSheetAppi - For custom date picker overlays