用 Flutter 做一个更顺手的日历组件:calendarview_flutter
做 App 的时候,日历组件经常看起来只是一个"小控件",但真正落到业务里会很快变复杂:单选、范围选择、多选、周/月切换、禁用日期、标记点、年视图、内容列表联动、不同业务视觉样式......如果每个项目都从零写一套,后期维护成本会非常高。
所以我整理并开源了一个 Flutter 日历组件:calendarview_flutter。它的目标不是只提供一个固定样式的日期选择器,而是把日历状态、交互能力和 UI 构建协议拆开,让业务可以更轻松地组合出自己的日历体验。
项目地址:github.com/raintang666...

它适合解决什么问题?
如果你的业务里有下面这些需求,应该会比较适合:
- 需要单选、范围选择、多选等不同日期选择模式
- 需要月视图和周视图切换
- 需要限制可选日期范围,比如只能选择未来 90 天
- 需要禁用部分日期,比如节假日、不可预约日期、已满日期
- 需要给日期添加标记,比如日程、小红点、状态标签
- 需要日历和内容列表联动
- 需要自定义日历单元格 UI,而不是被固定样式绑死
组件内部把核心状态放在 CalendarController 里,视图层通过 CalendarView、CalendarInteractiveView、CalendarMonthYearView 等组件来承载不同场景。这样业务代码可以更明确地控制状态,也更容易替换 UI。
快速接入
在项目中添加依赖:
yaml
dependencies:
calendarview_flutter: ^0.1.0
然后导入:
dart
import 'package:calendarview_flutter/calendarview_flutter.dart';
一个最小示例大概是这样:
dart
final controller = CalendarController(focusedDay: DateTime.now());
CalendarView(
controller: controller,
pageOrientation: CalendarPageOrientation.horizontal,
onDaySelected: (day) {
controller.selectDay(day);
},
)
这段代码里,CalendarController 负责保存当前聚焦日期、选中状态、选择模式、日期边界、标记数据等信息;CalendarView 负责展示和响应用户交互。
三种常见选择模式
日历选择是很多业务的核心。calendarview_flutter 目前支持单选、范围选择和多选。
单选适合生日、预约日期、签到日期等场景:
dart
controller
..setSelectionMode(CalendarSelectionMode.single)
..selectDay(DateTime(2026, 7, 14));
范围选择适合酒店入住、请假、周期筛选等场景:
dart
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) {},
)
多选适合打卡、排班、批量选择日期等场景:
dart
controller
..setSelectionMode(CalendarSelectionMode.multi)
..setMaxMultiSelectSize(5);
CalendarView(
controller: controller,
pageOrientation: CalendarPageOrientation.horizontal,
onDaySelected: (day) => controller.selectDay(day),
onMultiSelected: (day, selectedSize, maxSize) {},
onMultiSelectOutOfSize: (day, maxSize) {},
)

日期边界、禁用日期和标记
实际业务里,日历通常不是"所有日期都能点"。比如预约系统可能只能选择某个时间范围内的日期,票务系统可能要禁用售罄日期,日程系统则需要在某些日期上显示标记。
这些能力可以通过 controller 配置:
dart
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: '会议', color: Colors.blue),
],
});
这种方式的好处是:选择逻辑、日期限制和 UI 表现不需要混在一起。业务可以先把规则交给 controller,再由视图决定如何展示。
可展开、可折叠、可联动
除了基础月历,项目里还提供了 CalendarInteractiveView,用于处理更接近真实 App 的日历交互,比如:
- 月视图和周视图切换
- 日历折叠和展开
- 全屏展开
- 日历和内容列表联动
- 内容区纵向拖拽锁定
如果你的页面上方是日历,下方是对应日期的内容列表,这类结构就很适合用 CalendarInteractiveView 来承载。

年视图和自定义样式
很多日历组件做到月视图就结束了,但在需要快速跨月份跳转的场景里,年视图会非常有用。项目提供了 CalendarMonthYearView 和 CalendarMonthYearController,可以在日历上增加年视图能力。
同时,样式层并没有被写死。库里提供了 CalendarComponentBuilder 协议,业务可以自己实现星期栏、日期单元格、月份头等 UI:
dart
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}'));
}
}
也就是说,组件提供的是"日历能力"和"扩展协议",具体长什么样,可以交给业务决定。
最后
calendarview_flutter 目前还在持续完善中,欢迎大家试用、提 issue 或一起交流改进。后面我也会继续补充更多 demo、优化交互细节,并把实际业务里常见的日历场景沉淀到组件里。
GitHub 地址:github.com/raintang666...
如果这个项目刚好帮你省下一点时间,也欢迎点个 Star 支持一下。