【Flutter】table_calendarを使ったシンプルなアプリの作り方

# はじめに

この記事ではFlutterでカレンダーを表示するためのライブラリである
```table_calendar``` を使った簡単なアプリの実装方法を説明します。

機能としてはカレンダー上で選択した日付の値を別途表示します。
この基本的な機能をベースにTodoアプリなどに応用して頂ければと思います。

https://pub.dev/packages/table_calendar

# 開発環境

| tools/library       | ver    |
|-------------|--------|
| MacBook Pro | 11.6   |
| Flutter     | 2.5.4  |
| Dart        | 2.14.4 |
| table_calendar | 3.0.3 |


# 完成品とソースコード


### 完成品

完成品の仕様を説明します。
トップページは以下のような構成です。

* 「Calendar Selected App」という ```Text``` ウィジェット
* ```table_calendar``` ライブラリの ```TableCalendar``` ウィジェット
* ```TableCalendar``` ウィジェットで選択した日付を表示する ```Text```ウィジェット

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2256518/9e5c3dce-1b46-222b-b416-6ecdee537a69.png)

このアプリではページ遷移はありません。
ですのでFAB（FloatingActionButton）などのボタン類はありません。


todo:ここにスクショなど


### ソースコード

* pubspec.yaml

```yaml
table_calendar: ^3.0.3
```

* lib/main.dart

```dart

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'table_calendar app',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: TopPage());
  }
}

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

  @override
  _TopPageState createState() => _TopPageState();
}

class _TopPageState extends State<TopPage> {
  DateTime _selectedDay = DateTime.now();
  DateTime _focusedDay = DateTime.now();
  @override
  Widget build(BuildContext context) {
    double ui_height = MediaQuery.of(context).size.height;
    double ui_width = MediaQuery.of(context).size.width;

    return Scaffold(
      body: Container(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Center(
              child: Text(
                "Calendar Selected App",
                style: TextStyle(fontSize: 24, fontWeight: FontWeight.w800),
              ),
            ),
            SizedBox(
              height: ui_height * 0.05,
            ),
            Center(
              child: Container(
                  width: ui_width * 0.9,
                  height: ui_height * 0.5,
                  child: TableCalendar(
                    headerVisible: false,
                    firstDay: DateTime.utc(1900, 10, 16),
                    lastDay: DateTime.utc(2030, 3, 14),
                    focusedDay: _focusedDay,
                    selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
                    onDaySelected: (selectedDay, focusedDay) {
                      setState(() {
                        _selectedDay = selectedDay;
                      });
                    },
                  )),
            ),
            SizedBox(
              height: ui_height * 0.045,
            ),
            Container(
              child: Text(
                "selected year : ${_selectedDay.year}",
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
              ),
            ),
            Container(
              child: Text(
                "selected month : ${_selectedDay.month}",
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
              ),
            ),
            Container(
              child: Text(
                "selected day : ${_selectedDay.day}",
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
              ),
            ),
          ],
        ),
      ),
    );
  }
}


```



# ソースコード説明

本アプリでの```table_calendar``` の使い方を簡単に説明します。


まず ```table_calendar```を利用するためにライブラリをimportします。

```dart

import 'package:table_calendar/table_calendar.dart';

```

次に選択している日付オブジェクトを用意します。
本アプリでは一旦アプリを開いた当日の ```DateTime``` オブジェクトにしています。

```dart

  DateTime _selectedDay = DateTime.now();
  DateTime _focusedDay = DateTime.now();

```

https://api.flutter.dev/flutter/dart-core/DateTime-class.html

次に ```TableCalendar``` ウィジェットを用意します。

```dart

TableCalendar(
  headerVisible: false,
  firstDay: DateTime.utc(1900, 10, 16),
  lastDay: DateTime.utc(2030, 3, 14),
  focusedDay: _focusedDay,
  selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
  onDaySelected: (selectedDay, focusedDay) {
    setState(() {
      _selectedDay = selectedDay;
    });
  },
)

```

```headerVisible``` を ```false``` にすることでヘッダーを非表示にします。
このプロパティはデフォルトで ```true``` なので必要に応じて設定してください。
今回はヘッダーが不要だったので ```false``` にしています。

```firstDay``` プロパティはカレンダーで表示可能な最も古い日付を指定します。

```lastDay``` プロパティはカレンダーで表示可能な将来の日付を指定します。

```focusedDay``` では表示をする日付を ```DateTime``` オブジェクトで渡します。今回は ```_focusedDay``` で指定します。

```selectedDayPredicate``` ではコールバック関数を受け取ります。
役割は「 ```true``` になった時に日付が変わったと判定する」というものです。ですので引数で受けた ```day``` と現在選択している 
```_selectedDay``` を比較して一致した場合に ```isSameDay()``` メソッドから ```true``` を受け取ります。

最後に ```onDaySelected``` プロパティでは ```selectedDayPredicate``` で ```true``` になった際に
どのような処理を行うかを記述します。今回は ```_selectedDay``` を更新します。


また ```_selectedDay``` の値を表示に反映しているウィジェットは以下です。

```dart

Container(
  child: Text(
    "selected year : ${_selectedDay.year}",
    style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
  ),
),
Container(
  child: Text(
    "selected month : ${_selectedDay.month}",
    style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
  ),
),
Container(
  child: Text(
    "selected day : ${_selectedDay.day}",
    style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
  ),
),

```

表示に反映する ```_selectedDay``` を式内展開で ```Text``` ウィジェットに渡しています。

式内展開とは英語では「String interpolation」と呼ばれています。詳細は以下リンクをご参照ください。

https://dart.dev/guides/language/language-tour#a-basic-dart-program


# まとめ

本記事では ```table_calendar``` の ```TableCalendar``` ウィジェットを使った簡単なアプリの作り方を説明しました。
今回は選択した日付を表示するだけ、というシンプルなアプリでした。なので実仕様に落とし込む際にはこの実装方法を応用して頂ければと思います。

以上になります。