「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」。
本文翻译自pub: table_calendar | Flutter Package (flutter-io.cn)
译时版本: table_calendar 3.0.3
TableCalendar
高度可定制、功能丰富的 Flutter 日历组件。
| TableCalendar 使用自定义样式 | TableCalendar 使用自定义构建器 |
特性
- 大量易于使用的API
- 使用自定义样式预配置的 UI
- 自定义可选的构建器,可用于海量的 UI 设计
- 支持区域设置
- 支持范围选择
- 支持多选
- 动态事件和节假日
- 垂直方向自动调整大小 - 适应内容或填充 viewport
- 多种日历格式(月、双周、周)
- 水平方向滑动边界 (第一天、最后一天)
用法
安装
添加下面的行到 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 回调的 eventLoader 的 isSameDay 方法来实现:
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',
),
'en_US' | 'pl_PL' | 'fr_FR' | 'zh_CN' |
| 美式英语 | 波兰语 | 汉语 | 简体中文 |
注意,如果想改变 FormatButton 的文本,需要自己去做。这里使用 availableCalendarFormats 属性,然后传递翻译后的字符串。使用你选择的 i18n 方法。
也可以将 formatButtonVisible 设置为 false 来隐藏按钮。