用 Flutter 做一个更顺手的日历组件:calendarview_flutter

用 Flutter 做一个更顺手的日历组件:calendarview_flutter

做 App 的时候,日历组件经常看起来只是一个"小控件",但真正落到业务里会很快变复杂:单选、范围选择、多选、周/月切换、禁用日期、标记点、年视图、内容列表联动、不同业务视觉样式......如果每个项目都从零写一套,后期维护成本会非常高。

所以我整理并开源了一个 Flutter 日历组件:calendarview_flutter。它的目标不是只提供一个固定样式的日期选择器,而是把日历状态、交互能力和 UI 构建协议拆开,让业务可以更轻松地组合出自己的日历体验。

项目地址:github.com/raintang666...

它适合解决什么问题?

如果你的业务里有下面这些需求,应该会比较适合:

  • 需要单选、范围选择、多选等不同日期选择模式
  • 需要月视图和周视图切换
  • 需要限制可选日期范围,比如只能选择未来 90 天
  • 需要禁用部分日期,比如节假日、不可预约日期、已满日期
  • 需要给日期添加标记,比如日程、小红点、状态标签
  • 需要日历和内容列表联动
  • 需要自定义日历单元格 UI,而不是被固定样式绑死

组件内部把核心状态放在 CalendarController 里,视图层通过 CalendarViewCalendarInteractiveViewCalendarMonthYearView 等组件来承载不同场景。这样业务代码可以更明确地控制状态,也更容易替换 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 来承载。

年视图和自定义样式

很多日历组件做到月视图就结束了,但在需要快速跨月份跳转的场景里,年视图会非常有用。项目提供了 CalendarMonthYearViewCalendarMonthYearController,可以在日历上增加年视图能力。

同时,样式层并没有被写死。库里提供了 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 支持一下。

相关推荐
杉氧15 小时前
跨平台持久化:Flutter 本地数据库的多线程安全与架构设计实践
android·前端·flutter
yuanlaile15 小时前
Flutter 开发鸿蒙 App 踩坑总结,一套完整实战学习方案分享
flutter·harmonyos·flutter开发鸿蒙·flutter开发鸿蒙实战·flutter ai实战·鸿蒙 ai实战
1001101_QIA1 天前
flutter打包流程
flutter
GitLqr1 天前
Flutter FocusNode 实战指南:玩转键盘焦点与用户输入体验
android·flutter·ios
智玲君2 天前
如何在Flutter中集成Firebase A/B Testing
flutter
末代iOS程序员华仔2 天前
OPC + Flutter + Swift:跨平台应用上架与专业知识点获客实战指南
开发语言·flutter·swift
FungLeo2 天前
Flutter/Android Release 包连不上网?AndroidManifest INTERNET 权限排查实录
android·flutter
GitLqr2 天前
深度拆解 Dart 事件循环:从面试题看清 Microtask 与 Event Queue 的执行顺序
flutter·面试·dart
吾儿良辰3 天前
Flutter 迎来重大变革:Material 设计系统拆分,迈向更开放的 UI 框架
javascript·flutter·ui
看昭奚恤哭3 天前
Flutter 布局核心思想
开发语言·javascript·flutter