Horizontal List Calendar
A customizable horizontal calendar widget for Flutter. The HorizontalListCalendar widget allows you to display a horizontal list of dates, with customizable colors, text styles, and interaction. It's perfect for use cases like date pickers or calendar navigation.
Features
- Display dates in a horizontally scrolling list — month view or week view.
- Customizable selected, unselected, today, and disabled date styling.
- Supports custom text styles and locale-aware date/digit formatting.
minDate/maxDateranges and individually disabled dates.- Optional
HorizontalListCalendarControllerfor external control — jump to a date, listen for month/week changes, toggle view mode — no required setup if you don't need it. - Optional
dayBuilderfor fully custom day-cell rendering while the package still handles selection, scrolling, and constraints for you. - No required state-management dependency: state is handled internally via a plain
ChangeNotifier-based controller (same pattern asTextEditingController), so this package doesn't force Riverpod (or anything else) on your app.

Breaking changes in 2.0.0
If you're upgrading from 1.x, see CHANGELOG.md. Short version: flutter_riverpod is no longer a dependency, and scrollController is now optional instead of required.
Parameters
HorizontalListCalendar
| Parameter | Type | Description | Default Value |
|---|---|---|---|
onTap |
Function(DateTime) |
The callback function to handle date tap events. Required | N/A |
controller |
HorizontalListCalendarController? |
External controller for selection, view mode, and constraints. | null (an internal one is created and disposed for you) |
scrollController |
ScrollController? |
External scroll controller for the day list. | null (an internal one is created and disposed for you) |
onVisibleRangeChanged |
void Function(DateTime visibleAnchor)? |
Called when the visible month/week changes (not on every date tap). | null |
headerPadding |
EdgeInsets |
Padding around the calendar header. | EdgeInsets.zero |
locale |
String? |
Locale for date/digit formatting (e.g. 'bn', 'fr'). |
null (ambient default locale) |
bodyHeight |
double |
Height of the scrollable day-list area. | 70 |
itemWidth |
double |
Width per day cell (also used for scroll-to-selection). | 55 |
dayBuilder |
Widget Function(BuildContext, CalendarDayData)? |
Full override for how a day cell renders. | null (uses the built-in cell) |
| Selected Date Styling | |||
selectedColor |
Color |
The color of the selected date. | Colors.blue |
selectedFillColor |
Color |
Background fill color for the selected date. | Colors.transparent |
selectedTextStyle |
TextStyle |
Text style for the selected date. | TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.blue) |
| Unselected Date Styling | |||
unSelectedFillColor |
Color |
Background fill color for unselected dates. | Colors.transparent |
unSelectedTextStyle |
TextStyle |
Text style for unselected dates. | TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.blue) |
| Today's Date Styling | |||
todayFillColor |
Color |
Background color for today's date. | Colors.blue |
todayTextStyle |
TextStyle |
Text style for today's date. | TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.white) |
| Disabled Date Styling | |||
disabledFillColor |
Color? |
Background fill color for disabled dates. | null (falls back to unSelectedFillColor) |
disabledTextStyle |
TextStyle? |
Text style for disabled dates. | null (falls back to a dimmed unSelectedTextStyle) |
| Header Styling | |||
headerTextStyle |
TextStyle |
Text style for the header. | TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Colors.blue) |
| Icon Styling | |||
iconSize |
double |
Size of the navigation icons. | 18 |
moveToPreviousMonthIcon |
Icon? |
Custom icon for moving to the previous month/week. | null |
moveToPreviousMonthIconBackgroundColor |
Color |
Background color of the previous icon. | Colors.blue |
moveToPreviousMonthIconColor |
Color |
Color of the previous icon. | Colors.white |
moveToNextMonthIcon |
Icon? |
Custom icon for moving to the next month/week. | null |
moveToNextMonthIconBackgroundColor |
Color |
Background color of the next icon. | Colors.blue |
moveToNextMonthIconColor |
Color |
Color of the next icon. | Colors.white |
| Animation Parameters | |||
curve |
Curve |
The curve for the animation when switching months/weeks. | Curves.linear |
duration |
Duration |
Duration of the animation when switching months/weeks. | const Duration(milliseconds: 600) |
canSelectDate |
bool |
Turn date selection on or off. | true |
monthChangeButton |
bool |
Show prev/next navigation, or keep the current period fixed. | true |
HorizontalListCalendarController
Optional. Pass one in if you need external control; otherwise the widget manages its own.
| Parameter | Type | Description | Default Value |
|---|---|---|---|
initialDate |
DateTime? |
Date selected/visible on creation. | today |
minDate |
DateTime? |
Earliest selectable date (inclusive). | null (no limit) |
maxDate |
DateTime? |
Latest selectable date (inclusive). | null (no limit) |
disabledDates |
List<DateTime> |
Individually disabled dates. | [] |
initialViewMode |
CalendarViewMode |
.month or .week. |
CalendarViewMode.month |
startOfWeek |
StartOfWeek |
.monday or .sunday — only affects week view. |
StartOfWeek.monday |
Methods: jumpToDate(date), selectDate(date), nextMonth() / previousMonth(), nextWeek() / previousWeek(), setViewMode(mode), disableDate(date) / enableDate(date), isDateDisabled(date).
CalendarDayData (passed to dayBuilder)
| Field | Type | Description |
|---|---|---|
date |
DateTime |
The date this cell represents. |
isSelected |
bool |
Whether this date is currently selected. |
isToday |
bool |
Whether this date is today. |
isDisabled |
bool |
Whether this date is outside range or disabled. |
onSelect |
void Function() |
Call from your gesture handler to select this date. |
Example Usage
Basic — no controller needed:
HorizontalListCalendar(
onTap: (selectedDate) {
print('Selected Date: $selectedDate');
},
selectedColor: Colors.green,
selectedFillColor: Colors.yellow,
selectedTextStyle: TextStyle(color: Colors.red),
iconSize: 24,
moveToPreviousMonthIcon: Icon(Icons.arrow_back),
moveToNextMonthIcon: Icon(Icons.arrow_forward),
curve: Curves.easeInOut,
duration: Duration(milliseconds: 500),
)
With a controller — date range, disabled dates, and week view:
final controller = HorizontalListCalendarController(
minDate: DateTime(2026, 1, 1),
maxDate: DateTime(2026, 12, 31),
disabledDates: [DateTime(2026, 12, 25)],
initialViewMode: CalendarViewMode.week,
);
HorizontalListCalendar(
controller: controller,
onTap: (selectedDate) => print('Selected: $selectedDate'),
onVisibleRangeChanged: (anchor) => print('Now viewing: $anchor'),
)
// Elsewhere:
controller.jumpToDate(DateTime.now());
With locale (remember to call initializeDateFormatting(locale) in main() first — see below):
HorizontalListCalendar(
onTap: (selectedDate) {},
locale: 'bn',
)
// main.dart
import 'package:intl/date_symbol_data_local.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeDateFormatting('bn');
runApp(const MyApp());
}
With a custom day cell:
HorizontalListCalendar(
onTap: (selectedDate) {},
dayBuilder: (context, data) {
return GestureDetector(
onTap: data.onSelect,
child: Container(
width: 45,
decoration: BoxDecoration(
color: data.isSelected ? Colors.blue : Colors.grey.shade200,
borderRadius: BorderRadius.circular(8),
),
alignment: Alignment.center,
child: Text('${data.date.day}'),
),
);
},
)