table_calendar_tz 1.0.0
table_calendar_tz: ^1.0.0 copied to clipboard
Highly customizable, feature-packed calendar widget for Flutter with correct timezone and DST support. Fork of table_calendar.
TableCalendar TZ #
A fork of table_calendar with correct timezone and DST support.
Highly customizable, feature-packed calendar widget for Flutter.
![]() |
![]() |
|---|---|
| TableCalendar with custom styles | TableCalendar with custom builders |
Why this fork? #
The original table_calendar uses Duration-based arithmetic (subtract/add) to compute visible date ranges. This operates on absolute seconds rather than calendar days, which produces incorrect results when the calculation crosses a DST (Daylight Saving Time) boundary.
For example, in European timezones the spring-forward on March 29, 2026 makes that day only 23 hours long. When the calendar subtracts 3 days from April 1 to find the first visible Sunday, it overshoots by 1 hour and lands on March 28 instead of March 29 — shifting the entire month grid by one column.
This fork replaces all Duration arithmetic with calendar-aware date construction (DateTime.utc(y, m, d) / TZDateTime(loc, y, m, d)), which always produces midnight on the correct day regardless of DST transitions.
Features #
- Everything from the original
table_calendarpackage - Correct grid layout when using the
timeZoneparameter across DST boundaries - Regression tests covering spring-forward scenarios
Installation #
Add the following to your pubspec.yaml:
dependencies:
table_calendar_tz: ^4.1.0
Or use a Git dependency:
dependencies:
table_calendar_tz:
git:
url: https://github.com/arkarmintun1/table_calendar_tz.git
Usage #
The API is identical to the original table_calendar. Just update your import:
import 'package:table_calendar_tz/table_calendar_tz.dart';
Basic setup #
TableCalendar(
firstDay: DateTime.utc(2010, 10, 16),
lastDay: DateTime.utc(2030, 3, 14),
focusedDay: DateTime.now(),
);
With timezone support #
import 'package:timezone/data/latest.dart' as tz_data;
import 'package:timezone/timezone.dart' as tz;
// Initialize timezone data once at app startup
tz_data.initializeTimeZones();
final location = tz.getLocation('Europe/Berlin');
TableCalendar(
firstDay: tz.TZDateTime(location, 2010, 10, 16),
lastDay: tz.TZDateTime(location, 2030, 3, 14),
focusedDay: tz.TZDateTime.now(location),
timeZone: location,
);
Adding interactivity #
selectedDayPredicate: (day) {
return isSameDay(_selectedDay, day);
},
onDaySelected: (selectedDay, focusedDay) {
setState(() {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
});
},
onFormatChanged: (format) {
setState(() {
_calendarFormat = format;
});
},
onPageChanged: (focusedDay) {
_focusedDay = focusedDay;
},
Events #
Supply events via eventLoader:
eventLoader: (day) {
return _getEventsForDay(day);
},
When using a Map<DateTime, List<T>>, use a LinkedHashMap with isSameDay for correct equality:
final events = LinkedHashMap(
equals: isSameDay,
hashCode: getHashCode,
)..addAll(eventSource);
Custom UI with CalendarBuilders #
Use CalendarBuilders to selectively override any part of the UI:
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 #
TableCalendar(
locale: 'pl_PL',
);
Initialize date formatting first:
import 'package:intl/date_symbol_data_local.dart';
void main() {
initializeDateFormatting().then((_) => runApp(MyApp()));
}
![]() |
![]() |
![]() |
![]() |
|---|---|---|---|
'en_US' |
'pl_PL' |
'fr_FR' |
'zh_CN' |
Credits #
Based on table_calendar by Aleksander Woźniak.





