[译]Flutter日历组件table_calendar

7,284 阅读5分钟

「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」。

本文翻译自pub: table_calendar | Flutter Package (flutter-io.cn)

译时版本: table_calendar 3.0.3


TableCalendar

高度可定制、功能丰富的 Flutter 日历组件。

table_calendar_styles.giftable_calendar_builders.gif
TableCalendar 使用自定义样式TableCalendar 使用自定义构建器

特性

  • 大量易于使用的API
  • 使用自定义样式预配置的 UI
  • 自定义可选的构建器,可用于海量的 UI 设计
  • 支持区域设置
  • 支持范围选择
  • 支持多选
  • 动态事件和节假日
  • 垂直方向自动调整大小 - 适应内容或填充 viewport
  • 多种日历格式(月、双周、周)
  • 水平方向滑动边界 (第一天、最后一天)

用法

确保查看示例 和 API docs 中的更多详细内容。

安装

添加下面的行到 pubspec.yaml 中:

dependencies:
  table_calendar: ^3.0.3

基本设置

这里 有一个完整的示例。

TableCalendar 需要你提供 firstDay (第一天) 、 lastDay (最后一天) 和 focusedDay (选中的日期) :

  • firstDay 是日历上第一个可用的日期。用户无法访问该日期之间的日期。
  • lastDay 是日历上最后一个可用的日期。用户无法访问该日期之后的日期。
  • focusedDay 是当前选中的日期。使用该属性可以决定当前可看到的月份。
TableCalendar(
  firstDay: DateTime.utc(2010, 10, 16),
  lastDay: DateTime.utc(2030, 3, 14),
  focusedDay: DateTime.now(),
);

添加交互

你一定会注意到前面设置的日历不完全是可交互的 - 你只能水平来滑动它,来改变当前可见的月份。在一些场合,这样很低效。你可以通过指定一些回调轻松使其更有生机。

为日历组件添加下面的代码后使其可响应用户点击,并将点击的日期改为选中状态: Adding the following code to the calendar widget will allow it to respond to user's taps, marking the tapped day as selected:

selectedDayPredicate: (day) {
  return isSameDay(_selectedDay, day);
},
onDaySelected: (selectedDay, focusedDay) {
  setState(() {
    _selectedDay = selectedDay;
    _focusedDay = focusedDay; // update `_focusedDay` here as well
  });
},

要动态更新可见的日历格式,可为组件添加下面这些行: In order to dynamically update visible calendar format, add those lines to the widget:

calendarFormat: _calendarFormat,
onFormatChanged: (format) {
  setState(() {
    _calendarFormat = format;
  });
},

这两处更改会使日历可交互并响应用户的输入。

更新选中的日期

focusedDay 设置为一个静态值意味着无论何时 TableCalendar 组件重新构建, 它都会使用指定的 focusedDay 。你可以通过加载快速测试这一点:将 focusedDay 设置为 DateTime.now() ,滑动到下个月然后触发热加载 - 日期会 “重置” 到它的初始状态。要避免出现这个现象,你可以在任意回调暴露时存储和更新 focusedDay 。

添加下面这个回调以完成基本的设置:

onPageChanged: (focusedDay) {
  _focusedDay = focusedDay;
},

不需要在 onPageChanged() 回调里调用 setState(),这样做没有什么意义。 你只需要更新存储的值,这样如果之后组件重新构建的话,它会使用 focusedDay 属性。

这里有完整的示例。你可以在这里找到其它示例

事件

这里有完整的示例。

可以为 TableCalendar 提供自定义事件。要做到这一点,可以使用 eventLoader 属性 - 你会接收一个 DateTime 对象,它需要指定给一些事件。

eventLoader: (day) {
  return _getEventsForDay(day);
},

_getEventsForDay() 可以是任意实现。例如,可以使用 Map<DateTime, List<T>> :

List<Event> _getEventsForDay(DateTime day) {
  return events[day] ?? [];
}

一件值得记住的事情是 DateTime 对象由日期部分和时间部分组成。在一些场合,对于日历相关的维度来说,时间部分是多余的。

如果你决定使用 Map ,建议定义为 LinkedHashMap - 这会使你能够覆写两个  DateTime 对象的相等比较,只通过它们的日期部分进行比较:

final events = LinkedHashMap(
  equals: isSameDay,
  hashCode: getHashCode,
)..addAll(eventSource);

循环事件

eventLoader 可以让你轻松地添加某个模式下的重复事件。例如,下面的代码会为每个周一添加一个事件:

eventLoader: (day) {
  if (day.weekday == DateTime.monday) {
    return [Event('Cyclic event')];
  }

  return [];
},

点击时选中的多个事件

在点击选中一个目标日期时,时间通常会有一个事件子列表。可以通过提供给内置于 onDaySelected 回调的 eventLoaderisSameDay 方法来实现:

void _onDaySelected(DateTime selectedDay, DateTime focusedDay) {
  if (!isSameDay(_selectedDay, selectedDay)) {
    setState(() {
      _focusedDay = focusedDay;
      _selectedDay = selectedDay;
      _selectedEvents = _getEventsForDay(selectedDay);
    });
  }
}

这里有完整的示例

使用 CalendarBuilders 定制 UI

要使用你自己的组件定制 UI ,可以使用 CalendarBuilders 。 每一个构建器可用来选择性地覆写 UI ,使你可以用最小代价来实现高度定制的设计。

你可以从任意构建器返回 null 以使用默认样式。例如,下面的代码片段只会覆写一周中周日的显示标签(Sun),而其它日期的标签保持不变:

calendarBuilders: CalendarBuilders(
  dowBuilder: (context, day) {
    if (day.weekday == DateTime.sunday) {
      final text = DateFormat.E().format(day);

      return Center(
        child: Text(
          text,
          style: TextStyle(color: Colors.red),
        ),
      );
    }
  },
),

Locale(区域设置)

要在要求的语言下显示日历,使用 locale 属性。如果不指定的话,会使用默认的区域设置。

初始化

在使用一个区域设置之前,你可能需要初始化日期格式。

下面是一个简单的实现方式:

  • 首先,添加 intl 包到pubspec.yaml 文件
  • 然后修改 main() :
import 'package:intl/date_symbol_data_local.dart';

void main() {
  initializeDateFormatting().then((_) => runApp(MyApp()));
}

在这两步之后,你的应用就做好在不同的语言下使用 TableCalendar 的准备了。

指定一个语言

要指定一个语言,只需要简单地将其作为一个字符串码设置给 locale 属性。

例如,下面的代码会使 TableCalendar 使用波兰语:

TableCalendar(
  locale: 'pl_PL',
),
image.pngimage.pngimage.pngimage.png
'en_US''pl_PL''fr_FR''zh_CN'
美式英语波兰语汉语简体中文

注意,如果想改变 FormatButton 的文本,需要自己去做。这里使用 availableCalendarFormats 属性,然后传递翻译后的字符串。使用你选择的 i18n 方法。

也可以将  formatButtonVisible 设置为 false 来隐藏按钮。