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

328 阅读7分钟

1. 简介

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

2. 基础配置

安装

dependencies:
  table_calendar: ^3.0.1  # 请使用最新版本

最简实现

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. 核心基础属性

属性名类型说明
firstDayDateTime日历显示的起始日期(必传)
lastDayDateTime日历显示的结束日期(必传)
focusedDayDateTime当前聚焦的日期(必传,控制日历滚动位置)
selectedDayPredicatebool Function(DateTime)判断日期是否被选中的条件
onDaySelectedvoid Function(DateTime, DateTime)单个日期选中回调(选中日期+聚焦日期)
onPageChangedvoid Function(DateTime)日历页面切换时回调(返回新聚焦日期)

3.2. 视图控制属性

属性名类型说明
calendarFormatCalendarFormat日历展示格式(month/week/twoWeeks)
onFormatChangedvoid Function(CalendarFormat)视图格式切换回调
availableCalendarFormatsMap<CalendarFormat, String>允许的视图格式及对应显示文本
pageJumpingEnabledbool是否允许点击表头月份快速跳转页面(默认true)
weekNumbersVisiblebool是否显示周数(默认false)
weekNumberStyleTextStyle周数文本样式

示例:

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

3.3. 范围选择属性

属性名类型说明
rangeStartDayDateTime?范围选择的起始日期
rangeEndDayDateTime?范围选择的结束日期
rangeSelectionModeRangeSelectionMode范围选择模式(toggledOff/toggledOn/forced)
onRangeSelectedvoid Function(DateTime?, DateTime?, DateTime)范围选择回调(起始/结束/聚焦日期)
rangeDayPredicatebool Function(DateTime)判断日期是否允许被纳入范围选择

示例:

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. 事件与标记属性

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

示例:

// 定义事件数据结构
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. 样式定制属性

属性名类型说明
calendarStyleCalendarStyle日历主体样式配置
headerStyleHeaderStyle表头(月份栏)样式配置
daysOfWeekStyleDaysOfWeekStyle星期标题样式配置
rowHeightdouble日历行高(影响整体高度)
columnWidthdouble?日历列宽(null时自动计算)
paddingEdgeInsets日历内边距
marginEdgeInsets日历外边距
clipBehaviorClip裁剪行为(默认Clip.hardEdge)

CalendarStyle 子属性

属性名类型说明
defaultTextStyleTextStyle默认日期文本样式
selectedTextStyleTextStyle选中日期文本样式
todayTextStyleTextStyle今天日期文本样式
weekendTextStyleTextStyle周末日期文本样式
outsideTextStyleTextStyle显示范围外日期文本样式
disabledTextStyleTextStyle禁用日期文本样式
selectedDecorationDecoration选中日期装饰(背景等)
todayDecorationDecoration今天日期装饰
defaultDecorationDecoration默认日期装饰
weekendDecorationDecoration周末日期装饰
outsideDecorationDecoration范围外日期装饰
disabledDecorationDecoration禁用日期装饰
rangeStartDecorationDecoration范围选择起始日期装饰
rangeEndDecorationDecoration范围选择结束日期装饰
rangeMiddleDecorationDecoration范围选择中间日期装饰
markersDecorationDecoration事件标记容器装饰
markersMaxCountint最多显示的事件标记数量
markerSizedouble事件标记大小
markersOffsetPositionedOffset事件标记偏移量
canMarkersOverflowbool标记是否允许溢出日期单元格
outsideDaysVisiblebool是否显示范围外的日期(默认true)

HeaderStyle 子属性

属性名类型说明
formatButtonVisiblebool是否显示视图切换按钮(默认true)
formatButtonShowsNextbool切换按钮是否显示下一个格式(默认true)
formatButtonDecorationBoxDecoration视图切换按钮装饰
formatButtonTextStyleTextStyle视图切换按钮文本样式
leftChevronIconWidget左箭头图标
rightChevronIconWidget右箭头图标
titleTextStyleTextStyle标题(月份)文本样式
titleCenteredbool标题是否居中(默认false)
titleFormatterString Function(DateTime, dynamic)标题文本格式化器
headerPaddingEdgeInsets表头内边距
headerMarginEdgeInsets表头外边距
chevronPaddingEdgeInsets箭头图标内边距
chevronVisiblebool是否显示箭头图标(默认true)

样式定制示例:

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. 本地化属性

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

示例:

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. 交互控制属性

属性名类型说明
enabledDayPredicatebool Function(DateTime)判断日期是否可交互(默认全部可交互)
onHeaderTappedvoid Function(DateTime)表头点击回调
onHeaderLongPressedvoid Function(DateTime)表头长按回调
onDayLongPressedvoid Function(DateTime, DateTime)日期长按回调
dragStartBehaviorDragStartBehavior拖拽起始行为(默认start)

3.8. 高级自定义属性

属性名类型说明
calendarBuildersCalendarBuilders自定义构建器集合(以下为常用子项)
- dayBuilderWidget Function(BuildContext, DateTime, DateTime, bool, bool, bool, bool, List)自定义日期单元格
- markerBuilderWidget Function(BuildContext, DateTime, List)自定义事件标记
- headerTitleBuilderWidget Function(BuildContext, DateTime)自定义表头标题
- weekNumberBuilderWidget Function(BuildContext, int)自定义周数显示
transitionDurationDuration视图切换动画时长(默认200ms)
pageAnimationEnabledbool是否启用页面切换动画(默认true)

4. 性能优化

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

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

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

5. 完整配置示例

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();有惊喜哦~

往期文章