🐶

【Flutter】table_calendarでカレンダー作成

2023/03/09に公開
2

Flutterでカレンダーを作成するのに王道なtable_calendarのライブラリを使った実装を解説していきたいと思います。(2023.03現在)
今回使用したバージョンは下記の通りです。

Flutter 3.0.5
Dart 2.17.6  
table_calendar:  3.0.9

完成イメージ

パッケージのインストール

まずは、どのパッケージでもお馴染みのpubspec.yamlファイルに設定を書き込みんで「flutter pub get」コマンドを実行します。

environment:
  sdk: ">=2.17.6 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  table_calendar: ^3.0.9

カレンダーの表示

まずはシンプルなカレンダーを表示します。
以下の実装でカレンダーの表示が可能です。

focesedDayは今日に日付に印がつきます。

DateTime _focusedDay = DateTime.now();

TableCalendar(
  firstDay: DateTime.utc(2023, 1, 1),
  lastDay: DateTime.utc(2024, 12, 31),
  focusedDay: _focusedDay,
);

カレンダーフォーマット

次にカレンダーの表示を「1週間,2週間,1ヶ月」と3つに変更できるように実装します。
デフォルトを1ヶ月の表示に設定しています。

CalendarFormat _calendarFormat = CalendarFormat.month; // 月フォーマット               

onFormatChanged: (format) {  // 「月」「週」変更
                  if (_calendarFormat != format) {
                    setState(() {
                      _calendarFormat = format;
                    });
                  }
                }

日付の選択

selectedDayPredicate:を使用してどの日が現在選択されているかを設定します。
今回はisSameDay関数を返す実装にしています。
isSameDayは2つの引数(DateTime)を取り、2つの日付が同じか否かを判定します。

DateTime? _selectedDay; // 選択している日付 

          // 選択日のアニメーション
                selectedDayPredicate: (day) {
              return isSameDay(_selectedDay, day);
            },

加えてももう一つ下記の実装が必要になります。

                // 日付が選択されたときの処理
                onDaySelected: (selectedDay, focusedDay) {
                  setState(() {
                    _selectedDay = selectedDay;
                    _focusedDay = focusedDay;
                  });
                }

onDaySelectedはカレンダーがタップされるたびに呼ばれる関数で、そこにタップした際の日付(_selectedDay)を設定します。

イベントドットの表示

ここからは少しだけ応用です。
自分がドットマークをつけたい日付を宣言し、eventLoaderに返してあげればドットが表示されます。

  //Map形式で保持 keyが日付 値が文字列
  final sampleMap = {
    DateTime.utc(2023, 2,20): ['firstEvent', 'secondEvent'],
    DateTime.utc(2023, 2,5): ['thirdEvent', 'fourthEvent'],
  };

  eventLoader: (date) { // イベントドット処理
     return sampleMap[date] ?? []
         },

イベントの表示

さらにイベントをTextなどに表示したい場合も少し触れます。
Textなどに表示したい日付を宣言し _selectedEvents に代入してあげれば可能です。

List<String> _selectedEvents = []; 

 final sampleEvents = {
    DateTime.utc(2023, 2,20): ['firstEvent', 'secondEvent'],
    DateTime.utc(2023, 2,5): ['thirdEvent', 'fourthEvent']
  };

                // 日付が選択されたときの処理
                onDaySelected: (selectedDay, focusedDay) {
                  setState(() {
                    _selectedDay = selectedDay;
                    _focusedDay = focusedDay;
                    _selectedEvents = sampleEvents[selectedDay] ?? []; // ココを追加
                  });
                }

あとは、_selectedEventsに格納されたものを好きなものに表示させればOKです。
今回はListTileに表示させます。

          // タップした時表示するリスト
          Expanded(
            child: ListView.builder(
              itemCount: _selectedEvents.length,
              itemBuilder: (context, index) {
                final event = _selectedEvents[index];
                return Card(
                  child: ListTile(
                    title: Text(event),
                  ),
                );
              },
            ),
          ),

全体のコード

全体のコードはこちらになります。

import 'dart:collection';

import 'package:flutter/material.dart';
import 'package:table_calendar/table_calendar.dart';

class CalendarPage extends StatefulWidget {
  const CalendarPage({Key? key}) : super(key: key);

  
  State<CalendarPage> createState() => _CalendarPageState();
}

class _CalendarPageState extends State<CalendarPage> {
  DateTime _focusedDay = DateTime.now(); // 現在日
  CalendarFormat _calendarFormat = CalendarFormat.month; // 月フォーマット
  DateTime? _selectedDay; // 選択している日付
  List<String> _selectedEvents = [];

  //Map形式で保持 keyが日付 値が文字列
  final sampleMap = {
    DateTime.utc(2023, 2,20): ['firstEvent', 'secondEvent'],
    DateTime.utc(2023, 2,5): ['thirdEvent', 'fourthEvent'],
  };

  final sampleEvents = {
    DateTime.utc(2023, 2,20): ['firstEvent', 'secondEvent'],
    DateTime.utc(2023, 2,5): ['thirdEvent', 'fourthEvent']
  };

  
  Widget build(BuildContext context) {
    return Scaffold(
      // カレンダーUI実装
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(20.0),
            child: TableCalendar(
                firstDay: DateTime.utc(2023, 1, 1),
                lastDay: DateTime.utc(2024, 12, 31),
                focusedDay: _focusedDay,
                eventLoader: (date) { // イベントドット処理
                  return sampleMap[date] ?? [];
                },
                calendarFormat: _calendarFormat, // デフォを月表示に設定
                onFormatChanged: (format) {  // 「月」「週」変更
                  if (_calendarFormat != format) {
                    setState(() {
                      _calendarFormat = format;
                    });
                  }
                },
                // 選択日のアニメーション
                selectedDayPredicate: (day) {
              return isSameDay(_selectedDay, day);
            },
                // 日付が選択されたときの処理
                onDaySelected: (selectedDay, focusedDay) {
                  setState(() {
                    _selectedDay = selectedDay;
                    _focusedDay = focusedDay;
                    _selectedEvents = sampleEvents[selectedDay] ?? [];
                  });
                }
                ),
          ),
          // タップした時表示するリスト
          Expanded(
            child: ListView.builder(
              itemCount: _selectedEvents.length,
              itemBuilder: (context, index) {
                final event = _selectedEvents[index];
                return Card(
                  child: ListTile(
                    title: Text(event),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

最後に

以上がシンプルなカレンダーをtable_calendarを使用して実装になります。
どなたか参考になれば幸いです。

Discussion

kokikoki

記事とても参考になりました。ありがとうございます

1点だけ全体のコードでクラス名にタイポがあったので確認いただければと(気付いたので一応ということで)
Calender -> Calendar

katsukatsu

ご指摘ありがとうございます。
修正いたしましたmm