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 / maxDate ranges and individually disabled dates.
  • Optional HorizontalListCalendarController for external control — jump to a date, listen for month/week changes, toggle view mode — no required setup if you don't need it.
  • Optional dayBuilder for 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 as TextEditingController), so this package doesn't force Riverpod (or anything else) on your app.

Horizontal List Calendar Example

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}'),
      ),
    );
  },
)