Flutter自定义日历table_calendar完全指南+案例

1. 简介

table_calendar 是 Flutter 生态中功能强大的日历组件,支持多种视图切换、事件标记、日期范围选择等核心功能,适合需集成日历功能的应用场景(如日程管理、预约系统等)。其高度可定制化的特性使其能适配不同的 UI 需求。

2. 基础配置

安装

yaml 复制代码
dependencies:
  table_calendar: ^3.0.1  # 请使用最新版本

最简实现

dart 复制代码
import 'package:table_calendar/table_calendar.dart';

TableCalendar(
  firstDay: DateTime(2020),
  lastDay: DateTime(2030),
  focusedDay: DateTime.now(),
  selectedDayPredicate: (day) {
    return isSameDay(_selectedDay, day);
  },
  onDaySelected: (selectedDay, focusedDay) {
    setState(() {
      _selectedDay = selectedDay;
      _focusedDay = focusedDay;
    });
  },
)

核心参数说明:

  • firstDay/lastDay:日历显示范围(必传)
  • focusedDay:当前聚焦的日期(控制滚动位置,必传)
  • selectedDayPredicate:判断日期是否被选中的条件
  • onDaySelected:日期选择回调(返回选中日期和新聚焦日期)

3. 核心属性全解析

3.1. 核心基础属性

属性名 类型 说明
firstDay DateTime 日历显示的起始日期(必传)
lastDay DateTime 日历显示的结束日期(必传)
focusedDay DateTime 当前聚焦的日期(必传,控制日历滚动位置)
selectedDayPredicate bool Function(DateTime) 判断日期是否被选中的条件
onDaySelected void Function(DateTime, DateTime) 单个日期选中回调(选中日期+聚焦日期)
onPageChanged void Function(DateTime) 日历页面切换时回调(返回新聚焦日期)

3.2. 视图控制属性

属性名 类型 说明
calendarFormat CalendarFormat 日历展示格式(month/week/twoWeeks)
onFormatChanged void Function(CalendarFormat) 视图格式切换回调
availableCalendarFormats Map<CalendarFormat, String> 允许的视图格式及对应显示文本
pageJumpingEnabled bool 是否允许点击表头月份快速跳转页面(默认true)
weekNumbersVisible bool 是否显示周数(默认false)
weekNumberStyle TextStyle 周数文本样式

示例:

dart 复制代码
TableCalendar(
  // ...基础参数
  calendarFormat: _calendarFormat,
  onFormatChanged: (format) {
    setState(() {
      _calendarFormat = format;
    });
  },
  availableCalendarFormats: {
    CalendarFormat.month: '月',
    CalendarFormat.week: '周',
    CalendarFormat.twoWeeks: '两周',
  },
)

3.3. 范围选择属性

属性名 类型 说明
rangeStartDay DateTime? 范围选择的起始日期
rangeEndDay DateTime? 范围选择的结束日期
rangeSelectionMode RangeSelectionMode 范围选择模式(toggledOff/toggledOn/forced)
onRangeSelected void Function(DateTime?, DateTime?, DateTime) 范围选择回调(起始/结束/聚焦日期)
rangeDayPredicate bool Function(DateTime) 判断日期是否允许被纳入范围选择

示例:

dart 复制代码
RangeSelectionMode _rangeSelectionMode = RangeSelectionMode.toggledOff;
DateTime? _rangeStart;
DateTime? _rangeEnd;

TableCalendar(
  // ...基础参数
  rangeStartDay: _rangeStart,
  rangeEndDay: _rangeEnd,
  rangeSelectionMode: _rangeSelectionMode,
  onRangeSelected: (start, end, focusedDay) {
    setState(() {
      _rangeStart = start;
      _rangeEnd = end;
      _rangeSelectionMode = RangeSelectionMode.toggledOn;
      _selectedDay = null;  // 清除单个选中
    });
  },
)

3.4. 事件与标记属性

属性名 类型 说明
eventLoader List<Object> Function(DateTime) 加载指定日期的事件数据
calendarBuilders CalendarBuilders 自定义日历元素构建器(标记/日期等)
holidayLoader List<Object> Function(DateTime) 加载指定日期的假日数据
eventFadeTransitionEnabled bool 事件标记是否启用淡入动画(默认true)

示例:

dart 复制代码
// 定义事件数据结构
final Map<DateTime, List> _events = {
  DateTime(2023, 10, 15): ['会议', '生日'],
  DateTime(2023, 10, 20): ['deadline'],
};

TableCalendar(
  // ...其他参数
  eventLoader: (day) {
    return _events[day] ?? [];
  },
  calendarBuilders: CalendarBuilders(
    markerBuilder: (context, date, events) {
      if (events.isEmpty) return SizedBox();
      return Positioned(
        bottom: 1,
        child: Container(
          width: 16,
          height: 4,
          decoration: BoxDecoration(
            color: Colors.blue,
            borderRadius: BorderRadius.circular(2),
          ),
        ),
      );
    },
  ),
)

3.5. 样式定制属性

属性名 类型 说明
calendarStyle CalendarStyle 日历主体样式配置
headerStyle HeaderStyle 表头(月份栏)样式配置
daysOfWeekStyle DaysOfWeekStyle 星期标题样式配置
rowHeight double 日历行高(影响整体高度)
columnWidth double? 日历列宽(null时自动计算)
padding EdgeInsets 日历内边距
margin EdgeInsets 日历外边距
clipBehavior Clip 裁剪行为(默认Clip.hardEdge)

CalendarStyle 子属性

属性名 类型 说明
defaultTextStyle TextStyle 默认日期文本样式
selectedTextStyle TextStyle 选中日期文本样式
todayTextStyle TextStyle 今天日期文本样式
weekendTextStyle TextStyle 周末日期文本样式
outsideTextStyle TextStyle 显示范围外日期文本样式
disabledTextStyle TextStyle 禁用日期文本样式
selectedDecoration Decoration 选中日期装饰(背景等)
todayDecoration Decoration 今天日期装饰
defaultDecoration Decoration 默认日期装饰
weekendDecoration Decoration 周末日期装饰
outsideDecoration Decoration 范围外日期装饰
disabledDecoration Decoration 禁用日期装饰
rangeStartDecoration Decoration 范围选择起始日期装饰
rangeEndDecoration Decoration 范围选择结束日期装饰
rangeMiddleDecoration Decoration 范围选择中间日期装饰
markersDecoration Decoration 事件标记容器装饰
markersMaxCount int 最多显示的事件标记数量
markerSize double 事件标记大小
markersOffset PositionedOffset 事件标记偏移量
canMarkersOverflow bool 标记是否允许溢出日期单元格
outsideDaysVisible bool 是否显示范围外的日期(默认true)

HeaderStyle 子属性

属性名 类型 说明
formatButtonVisible bool 是否显示视图切换按钮(默认true)
formatButtonShowsNext bool 切换按钮是否显示下一个格式(默认true)
formatButtonDecoration BoxDecoration 视图切换按钮装饰
formatButtonTextStyle TextStyle 视图切换按钮文本样式
leftChevronIcon Widget 左箭头图标
rightChevronIcon Widget 右箭头图标
titleTextStyle TextStyle 标题(月份)文本样式
titleCentered bool 标题是否居中(默认false)
titleFormatter String Function(DateTime, dynamic) 标题文本格式化器
headerPadding EdgeInsets 表头内边距
headerMargin EdgeInsets 表头外边距
chevronPadding EdgeInsets 箭头图标内边距
chevronVisible bool 是否显示箭头图标(默认true)

样式定制示例:

dart 复制代码
TableCalendar(
  // ...其他参数
  calendarStyle: CalendarStyle(
    // 选中日期样式
    selectedDecoration: BoxDecoration(
      color: Colors.blue,
      shape: BoxShape.circle,
    ),
    // 今天日期样式
    todayDecoration: BoxDecoration(
      color: Colors.grey[200],
      shape: BoxShape.circle,
    ),
    // 周末样式
    weekendTextStyle: TextStyle(color: Colors.red),
    // 事件标记样式
    markersMaxCount: 3,  // 最多显示3个标记
    markerSize: 6,
  ),
  // 表头样式
  headerStyle: HeaderStyle(
    formatButtonVisible: false,  // 隐藏视图切换按钮
    titleCentered: true,
    headerPadding: EdgeInsets.symmetric(vertical: 8),
    titleTextStyle: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
  ),
)

3.6. 本地化属性

属性名 类型 说明
locale String? 本地化语言代码(如'zh_CN'、'en_US')
daysOfWeekLabels List<String> 星期标签文本(默认按locale生成)
daysOfWeekLabelsExceptions Map<int, String> 特定星期几的文本覆盖
weekNumberLabel String 周数标签文本(默认'W')

示例:

dart 复制代码
import 'package:intl/intl.dart';
import 'package:intl/date_symbol_data_local.dart';

// 初始化本地化
initializeDateFormatting().then((_) {
  setState(() {});
});

TableCalendar(
  // ...其他参数
  locale: 'zh_CN',  // 支持 'en_US', 'ja_JP' 等
  daysOfWeekStyle: DaysOfWeekStyle(
    weekdayStyle: TextStyle(),
    weekendStyle: TextStyle(color: Colors.red),
  ),
)

3.7. 交互控制属性

属性名 类型 说明
enabledDayPredicate bool Function(DateTime) 判断日期是否可交互(默认全部可交互)
onHeaderTapped void Function(DateTime) 表头点击回调
onHeaderLongPressed void Function(DateTime) 表头长按回调
onDayLongPressed void Function(DateTime, DateTime) 日期长按回调
dragStartBehavior DragStartBehavior 拖拽起始行为(默认start)

3.8. 高级自定义属性

属性名 类型 说明
calendarBuilders CalendarBuilders 自定义构建器集合(以下为常用子项)
- dayBuilder Widget Function(BuildContext, DateTime, DateTime, bool, bool, bool, bool, List) 自定义日期单元格
- markerBuilder Widget Function(BuildContext, DateTime, List) 自定义事件标记
- headerTitleBuilder Widget Function(BuildContext, DateTime) 自定义表头标题
- weekNumberBuilder Widget Function(BuildContext, int) 自定义周数显示
transitionDuration Duration 视图切换动画时长(默认200ms)
pageAnimationEnabled bool 是否启用页面切换动画(默认true)

4. 性能优化

  • 使用 ValueNotifier 管理事件数据,避免不必要重建
  • 复杂标记使用缓存Widget
  • 合理设置 firstDaylastDay 范围
dart 复制代码
final ValueNotifier<List> _selectedEvents = ValueNotifier([]);

// 监听选中日期变化更新事件
void _updateSelectedEvents() {
  _selectedEvents.value = _events[_selectedDay] ?? [];
}

// 构建时使用ValueListenableBuilder
ValueListenableBuilder<List>(
  valueListenable: _selectedEvents,
  builder: (context, value, _) {
    return // 事件列表
  },
)

5. 完整配置示例

dart 复制代码
TableCalendar(
  firstDay: DateTime(2020),
  lastDay: DateTime(2030),
  focusedDay: _focusedDay,
  selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
  onDaySelected: (selectedDay, focusedDay) {
    if (!isSameDay(_selectedDay, selectedDay)) {
      setState(() {
        _selectedDay = selectedDay;
        _focusedDay = focusedDay;
      });
    }
  },
  // 视图配置
  calendarFormat: _calendarFormat,
  onFormatChanged: (format) => setState(() => _calendarFormat = format),
  availableCalendarFormats: const {
    CalendarFormat.month: '月',
    CalendarFormat.week: '周',
  },
  // 范围选择
  rangeStartDay: _rangeStart,
  rangeEndDay: _rangeEnd,
  rangeSelectionMode: _rangeSelectionMode,
  onRangeSelected: (start, end, focusedDay) => setState(() {
    _rangeStart = start;
    _rangeEnd = end;
    _rangeSelectionMode = RangeSelectionMode.toggledOn;
  }),
  // 事件加载
  eventLoader: (day) => _events[day] ?? [],
  // 样式定制
  calendarStyle: CalendarStyle(
    selectedDecoration: const BoxDecoration(
      color: Colors.blue,
      shape: BoxShape.circle,
    ),
    todayDecoration: BoxDecoration(
      color: Colors.grey[200],
      shape: BoxShape.circle,
    ),
    markersMaxCount: 3,
  ),
  headerStyle: const HeaderStyle(
    titleCentered: true,
    formatButtonVisible: false,
  ),
  // 本地化
  locale: 'zh_CN',
  // 自定义构建器
  calendarBuilders: CalendarBuilders(
    markerBuilder: (context, date, events) => events.isNotEmpty
        ? Container(
            width: 12,
            height: 12,
            decoration: const BoxDecoration(
              color: Colors.red,
              shape: BoxShape.circle,
            ),
          )
        : null,
  ),
)

6. 总结

table_calendar 提供了灵活的日历解决方案,核心关注:

  • 基础日期选择与范围选择的状态管理
  • 事件标记与自定义样式的视觉定制
  • 视图切换与本地化的用户体验优化
  • 性能优化技巧(如状态隔离、合理设置范围)

更复杂功能可参考官方文档Github仓库实现。使用时需注意范围选择与单个选择的互斥性,以及本地化配置的初始化步骤。


本次分享就到这儿啦,我是鹏多多,深耕前端的技术创作者,如果您看了觉得有帮助,欢迎评论,关注,点赞,转发,我们下次见~

PS:在本页按F12,在console中输入document.getElementsByClassName('panel-btn')[0].click();有惊喜哦~

往期文章

相关推荐
@菜菜_达10 小时前
前端 HTML 入门(标签)
前端·html
智航GIS10 小时前
7.1 自定义函数
前端·javascript·python
l1340620823510 小时前
Flutter Geocoding 在鸿蒙上的使用指南
flutter·华为·harmonyos
BlackWolfSky10 小时前
React中文网课程笔记1—快速入门
前端·笔记·react.js
A_one201010 小时前
利用npm内置命令构建脚本工具
前端·npm·node.js
哔哩哔哩技术10 小时前
2025年哔哩哔哩技术精选技术干货
前端·后端·架构
霍理迪10 小时前
CSS布局方式——定位
前端·css
冬奇Lab10 小时前
稳定性性能系列之六——Java异常与JE分析实战
android·性能优化·debug
星光不问赶路人10 小时前
TypeScript 架构实践:从后端接口到 UI 渲染数据流的完整方案
前端·vue.js·typescript
ttyyttemo10 小时前
Dagger技术的使用学习
前端