A Flutter Calendar That Splits State, Interaction, and UI So You Don't Have to Rewrite It Every Project
Building a More Flexible Calendar Component with Flutter: calendarview_flutter
When building apps, a calendar component often looks like just a "small widget," but it quickly becomes complex once it lands in real business scenarios: single selection, range selection, multi-selection, week/month switching, disabled dates, markers, year view, content list linkage, different business visual styles... If you build one from scratch for every project, the long-term maintenance cost becomes very high.
So I organized and open-sourced a Flutter calendar component: calendarview_flutter. Its goal is not just to provide a date picker with a fixed style, but to separate calendar state, interaction capabilities, and UI construction protocols, so that businesses can more easily compose their own calendar experience.
Project address: https://github.com/raintang666/BFCalendar

What problems is it suitable for solving?
If your business has the following requirements, it should be a good fit:
- Need different date selection modes like single, range, or multi-selection
- Need switching between month view and week view
- Need to limit the selectable date range, e.g., only allow selecting the next 90 days
- Need to disable some dates, e.g., holidays, non-bookable dates, fully booked dates
- Need to add markers to dates, e.g., schedules, red dots, status labels
- Need calendar and content list linkage
- Need to customize calendar cell UI instead of being locked into a fixed style
The component internally keeps core state in CalendarController, and the view layer uses components like CalendarView, CalendarInteractiveView, and CalendarMonthYearView to handle different scenarios. This way, business code can control state more explicitly and replace the UI more easily.
Quick Start
Add the dependency to your project:
dependencies:
calendarview_flutter: ^0.1.0
Then import:
import 'package:calendarview_flutter/calendarview_flutter.dart';
A minimal example looks roughly like this:
final controller = CalendarController(focusedDay: DateTime.now());
CalendarView(
controller: controller,
pageOrientation: CalendarPageOrientation.horizontal,
onDaySelected: (day) {
controller.selectDay(day);
},
)
In this code, CalendarController is responsible for storing the current focused date, selection state, selection mode, date boundaries, marker data, etc.; CalendarView is responsible for display and responding to user interactions.
Three Common Selection Modes
Calendar selection is the core of many businesses. calendarview_flutter currently supports single selection, range selection, and multi-selection.
Single selection is suitable for scenarios like birthdays, appointment dates, check-in dates:
controller
..setSelectionMode(CalendarSelectionMode.single)
..selectDay(DateTime(2026, 7, 14));
Range selection is suitable for scenarios like hotel check-in, leave requests, period filtering:
controller
..setSelectionMode(CalendarSelectionMode.range)
..setRangeSelectionLimits(minRange: 2, maxRange: 14);
CalendarView(
controller: controller,
pageOrientation: CalendarPageOrientation.horizontal,
onDaySelected: (day) => controller.selectDay(day),
onRangeSelected: (range) {
final start = range.start;
final end = range.end;
},
onSelectOutOfRange: (day, violation) {},
)
Multi-selection is suitable for scenarios like check-ins, shift scheduling, batch date selection:
controller
..setSelectionMode(CalendarSelectionMode.multi)
..setMaxMultiSelectSize(5);
CalendarView(
controller: controller,
pageOrientation: CalendarPageOrientation.horizontal,
onDaySelected: (day) => controller.selectDay(day),
onMultiSelected: (day, selectedSize, maxSize) {},
onMultiSelectOutOfSize: (day, maxSize) {},
)

Date Boundaries, Disabled Dates, and Markers
In real business, a calendar usually isn't one where "all dates can be tapped." For example, a booking system might only allow selecting dates within a certain time range, a ticketing system might need to disable sold-out dates, and a scheduling system needs to show markers on certain dates.
These capabilities can be configured through the controller:
controller
..setCalendarRange(
minDate: DateTime(2026, 7, 1),
maxDate: DateTime(2026, 12, 31),
)
..setDisabledDatePredicate((day) {
return day.weekday == DateTime.sunday;
})
..setMarkers({
DateTime(2026, 7, 15): [
CalendarMarker(label: 'Meeting', color: Colors.blue),
],
});
The advantage of this approach is that selection logic, date restrictions, and UI presentation don't need to be mixed together. The business can first hand the rules to the controller, and then the view decides how to display them.
Expandable, Collapsible, and Linkable
Beyond the basic month calendar, the project also provides CalendarInteractiveView for handling calendar interactions closer to real apps, such as:
- Switching between month view and week view
- Calendar collapsing and expanding
- Full-screen expansion
- Calendar and content list linkage
- Vertical drag locking in the content area
If your page has a calendar on top and a content list for the corresponding date below, this type of structure is very suitable for CalendarInteractiveView to handle.

Year View and Custom Styles
Many calendar components stop at the month view, but in scenarios requiring quick cross-month jumps, a year view is very useful. The project provides CalendarMonthYearView and CalendarMonthYearController, which can add year view capability to the calendar.
At the same time, the style layer is not hardcoded. The library provides a CalendarComponentBuilder protocol, allowing businesses to implement their own UI for week bars, date cells, month headers, etc.:
class MyCalendarBuilder extends CalendarComponentBuilder {
const MyCalendarBuilder();
@override
List<String> orderedWeekLabels(int firstWeekday) {
return calendarOrderedWeekLabels(
const ['日', '一', '二', '三', '四', '五', '六'],
firstWeekday,
);
}
@override
Widget buildWeekBarCell(BuildContext context, CalendarWeekBarCellData data) {
return Text(data.label);
}
@override
Widget buildDayCell(BuildContext context, CalendarDayCellData data) {
return Center(child: Text('${data.date.day}'));
}
}
In other words, the component provides "calendar capabilities" and "extension protocols"; what it actually looks like can be decided by the business.
Finally
calendarview_flutter is still under continuous improvement. Everyone is welcome to try it out, raise issues, or discuss improvements together. I will continue to add more demos, optimize interaction details, and solidify common business calendar scenarios into the component.
GitHub address: https://github.com/raintang666/BFCalendar
If this project happens to save you a bit of time, a Star would be very welcome support.
Top 1 of 2 from juejin.cn, machine-translated. The original thread is authoritative.
Impressive
[shy]