📆

FlutterのTableCalendarのselectedDay

2025/01/12に公開

個人開発でモバイルアプリを作る中で、ふーんと思ったことを記録。

概要

  • TableCalendarのonDaySelectedコールバックの引数であるselectedDayは、
    yyyy-MM-DD 00:00:00Zのように、選択した日が始まる時間(UTC)のDateTimeになっているっぽい。

やろうとしたこと

環境

  • Flutter 3.13.6
  • Dart 3.1.3
  • table_calendar 3.1.3

内容

  • 日記的なアプリで、カレンダーから日付を選択し、その日のエントリを追加する機能の実装
  • カレンダーから日付を選択し、その日のエントリ一覧を表示する機能の実装

問題のあったコード

詳細は省略しますが、_selectedDateを利用して選択した日の日記エントリを作成したり、選択した
日のエントリ一覧を取得して表示したりします。

カレンダーを一度も触っていない状態だと、_selectedDateにはlocaltimeの日時が入っているため、ちょうど_selectedDateに作成したエントリ(そんなものはない)しか表示されないという問題が発生しました。
(クエリを範囲指定にすればいいだけだった)

カレンダーを一度でも触ると、_selectedDateにはutcかつ00:00:00の値が入るので、作成したエントリには揃えられた日時が登録され、エントリの取得時に時間がずれて参照できなくなるということはありませんでした。

class _DiaryState extends State<Diary> {
  DateTime _selectedDate = DateTime.now();

  // 省略

  
  Widget build(BuildContext context) {

    // 省略

    TableCalendar(
      headerStyle: const HeaderStyle(
        formatButtonVisible: false,
      ),
      focusedDay: _selectedDate,
      firstDay: DateTime(2000),
      lastDay: DateTime(2100),
      calendarFormat: CalendarFormat.month,
      selectedDayPredicate: (day) => isSameDay(_selectedDate, day),
      onDaySelected: (selectedDay, focusedDay) {
        setState(() {
          _selectedDate = selectedDay;
        });
      },
    ),

    // 省略
  }
}

対応

今回、日記エントリには時間データは持たせず、日付だけを持たせるつもりでした。
日記エントリのモデルクラス側でフォーマットを調整することを考えましたが、TableCalendarのisSameDayメソッドの判定基準とかもまだよくわかっていないので、全部TableCalendarに合わせて00:00:00(utc)にすることで解決😇

class _DiaryState extends State<Diary> {
  DateTime _selectedDate = DateTime.now();

  _DiaryState() {
    // NOTE: TableCalendarのselectedDayはUTCの00:00:00なので、タイムゾーンを考慮して調整
    _selectedDate = DateTime(_selectedDate.year, _selectedDate.month, _selectedDate.day);
    Duration offset = _selectedDate.timeZoneOffset;
    _selectedDate = _selectedDate.add(offset).toUtc();
  }

  // 省略
}

おわり

TableCalendarはデザインがシンプルで気に入りました。

参考

https://pub.dev/packages/table_calendar

Discussion