ローソンデジタルイノベーション テックブログ

ローソンデジタルイノベーション(LDI)の技術ブログです

社内向けの在宅勤務日を管理するアプリで祝日を表示する

こんにちは、LDIのモバイルアプリエンジニアの庄司です。

今回はFlutterを使って社内向けアプリを作った際に利用したtable_calendarというライブラリでの実装について書いていきたいと思います。

なお、導入方法はいろんな方が説明してくださっているので省略し、祝日の表示のために行ったことに絞って書きます。

今回利用している関連ライブラリのバージョンは次のとおりです。

Flutter 3.7.7
Dart 2.19.2
table_calendar 3.0.8

社内向けアプリの簡単な紹介

LDIではコロナ禍を機に在宅勤務制度の利用者が増え、そのことにより勤怠管理が煩雑になものとなりました。
この問題に対処するため、在宅勤務報告システムをWebアプリとして開発しました。

祝日の表示

在宅勤務報告システムではカレンダーで勤務予定日を選択して申請する、ということができますが このカレンダーにいつが祝日であるかを表示する必要がありました。

日本の祝日のデータの取得

まず、祝日を表示するためには祝日のデータが必要です。 日本の祝日はGoogleのカレンダーAPIから取得することができたのでこちらを利用することにしました。

祝日の取得方法について

取得方法ですが、カレンダーAPIの利用にはGoogle Cloud コンソールでの設定が必要なため、
カレンダーAPIのクイックスタートのドキュメントに従いAPIを有効にしてAPIキーを作成する必要があります。

カレンダーAPI クイックスタート

なお、日本の祝日はGoogleアカウントの認証をせずに取得可能なため、OAuthの設定は不要です。

祝日のデータは日本の祝日カレンダーのイベントとして取得できます。
日本の祝日カレンダーのカレンダーIDは次のとおりです。

ja.japanese#holiday@group.v.calendar.google.com

カレンダーのイベントは次のURLでGETリクエストを行うことで取得できます。

https://www.googleapis.com/calendar/v3/calendars/$calendarId/events?key=$apiKey

$calendarIdには日本の祝日カレンダーのID、$apiKeyにはGoogle Cloud コンソールで作成したAPIキーを指定します。

レスポンスのitemsにイベントのリストとして祝日のデータが入っているのでこれを使います。

そのほかAPIの詳細については下記のドキュメントを参照してください。
Events: list  |  Google Calendar  |  Google for Developers

また、カレンダーIDはGoogleカレンダーの画面で各カレンダーの設定から確認することができます。

カレンダーの設定表示方法
Googleアカウントの認証不要で使えるカレンダーは日本の祝日のほかにも宗教毎の祝日や月の満ち欠けなどもあるようです。

実装例

今回は祝日の日付とその日の名称が必要だったので日付をキーにした祝日の名称のMapを作って管理することにしました。
table_calendarでは日付をUTCかつ0時0分で扱っているので、ここで生成するDateTimeはUTCかつ0時0分にしておくと便利です。 実装例は下記のようになります。

import 'package:http/http.dart' as http;

class HolidayRemoteRepository {
  final String _calendarId =
      Uri.encodeComponent('ja.japanese#holiday@group.v.calendar.google.com');
  final _apiKey = "作成したAPIキー";

  Future<Map<DateTime, String?>?> fetchHolidays() async {
    final String url =
        'https://www.googleapis.com/calendar/v3/calendars/$_calendarId/events?key=$_apiKey';
    final response = await http.get(Uri.parse(url));
    Map<DateTime, String> holidays = {};
    if (response.statusCode == 200) {
      final List<dynamic> items = json.decode(response.body)['items'];

      for (var item in items) {
        var date = DateTime.parse(item['start']['date']);
        // table_calendarではUTCかつ0時0分のDateTimeを使っているのでそれに合わせて変換
        // table_calendarが用意しているnormalizeDate()を使ってもよいかも
        var dateUtc = DateTime.utc(date.year, date.month, date.day);
        holidays[dateUtc] = item['summary'];
      }
      return holidays;
    } else {
      // Request failed
      return null;
    }
  }
}

祝日名の表示

table_calendarではTableCalendarクラスのプロパティeventLoaderで特定の日付のイベントを指定することができ、指定したイベントに応じて日付と共にマーカーを表示することができます。 イベントを示す値は任意の型を利用可能です。

このマーカーはCalendarBuildersクラスのプロパティmarkerBuilderで自由に変更することができます。

実装例

祝日名の表示は次のようなイメージで表示することにしました。

祝日の名称

pub.devのtable_calendarのページのサンプル画像ではマーカーは日付の文字の下に配置されていますが、 markerBuilderで指定可能な領域は日付の文字と重なっているため、適切なマージンの設定が必要です。

今回は祝日のデータをeventLoaderで指定し、markerBuilderで祝日の名称を表示しました。 以下実装例です。

 // 説明していないプロパティは省略しています
    TableCalendar<String>( // イベントを示す値の型安全を担保したい場合はここで型を宣言できる
      eventLoader: (date) { // dateはUTCかつ0時0分
         // カレンダーAPIで取得した祝日
         var holiday = holidays[date];
         return [holiday];
       },
      calendarBuilders: CalendarBuilders(
        markerBuilder: _markersBuilder,
      ),
    );

  static MarkerBuilder get _markersBuilder => (context, date, holidays) {
        var holiday = SizedBox(
            height: 20,
            child: Text(
              holidays.first,
              style: const TextStyle(fontSize: 10, color: Colors.red),
            ));

        return Positioned(
          top: 20,// 日付より下になるように調整
          child: holiday,
        );
      };

まとめ

  • 祝日の情報をGoogleのカレンダーAPIで取得し、カレンダーに祝日の名称を表示しました。

  • table_calendarでは日付をUTCかつ0時0分のDateTimeで扱っているのでDateTimeの生成は注意が必要です。

所感

実はこのアプリの作成でFlutter及びDartを初めて使用しました。 Dartの言語仕様は普段使っているKotlinやSwiftとは思想が違うところがあり、色々と驚きがありました。 名前付き引数を明示的に指定するのは良いと感じましたが、オーバーロードができないのは少し辛かったです。

最後に

今後もLDIのエンジニアが記事を投稿していくので、興味がある方はぜひ読者になるをお願いします。