【Flutter】table_calendarを使用してカレンダーを表示

flutter

1. 概要

上記では、Flutterでfreezedやgraphql-code-generatorでモデルクラスを自動生成しGraphQLClientを使ってGraphQLのAPIにアクセスし、データを取得する内容でした。今回は、table_calendarを使い、カレンダーを表示する内容となります。

対象としては開発を1年程やってて自分で最初から開発してみたい方になります。そのため細かい用語などの説明はしません。

2. プロジェクトの準備

2-1. プロジェクトを作成

3. Dependenciesの追加

3-1. table_calendarやprompt_dialogを追加

flutter pub add table_calendar
flutter pub add prompt_dialog

4. ソースコード

4-1. lib/utils/utils.dart

import 'dart:collection';
import 'package:table_calendar/table_calendar.dart';

class Event {
  final DateTime day;
  final String eventGuid;
  final String title;

  const Event(this.day, this.eventGuid, this.title);

  @override
  String toString() => title;
}

final kEvents = LinkedHashMap<DateTime, List<Event>>(
  equals: isSameDay,
  hashCode: getHashCode,
);

int getHashCode(DateTime key) {
  return key.day * 1000000 + key.month * 10000 + key.year;
}

final kToday = DateTime.now();
final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day);
final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day);

4-2. lib/pages/example.dart

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

class TableExample extends StatefulWidget {
  const TableExample({super.key});

  @override
  State<TableExample> createState() => _TableExampleState();
}

class _TableExampleState extends State<TableExample> {
  late final ValueNotifier<List<Event>> _selectedEvents;
  CalendarFormat _calendarFormat = CalendarFormat.month;
  RangeSelectionMode _rangeSelectionMode = RangeSelectionMode.toggledOff;
  DateTime _focusedDay = DateTime.now();
  DateTime? _selectedDay;
  int eventId = 0;

  @override
  void initState() {
    super.initState();

    _selectedDay = _focusedDay;

    List<Event> list = _createList();
    _selectedEvents = ValueNotifier(list);
  }

  String _getEventId() {
    return 'Event-${eventId++}';
  }

  List<Event> _createList() {
    List<Event> list = [];
    for (var elements in kEvents.values) {
      for (var e in elements) {
        list.add(e);
      }
    }
    return list;
  }

  @override
  void dispose() {
    _selectedEvents.dispose();
    super.dispose();
  }

  List<Event> _getEventsForDay(DateTime day) {
    return kEvents[day] ?? [];
  }

  void _onDaySelected(DateTime selectedDay, DateTime focusedDay) async {
    if (!isSameDay(_selectedDay, selectedDay)) {
      String? promptValue = await prompt(context);
      final newEvent = {
        selectedDay: [
          Event(selectedDay, _getEventId(), promptValue ?? ''),
        ],
      };
      kEvents.addAll(newEvent);
      _selectedEvents.value = _createList();

      setState(() {
        _selectedDay = selectedDay;
        _focusedDay = focusedDay;
        _rangeSelectionMode = RangeSelectionMode.toggledOff;
      });
    }
  }

  void _removeEvent(int idx, Event event) {
    List<Event> newList = [];
    List<Event> currentList = _getEventsForDay(event.day);
    for (var e in currentList) {
      if (e.eventGuid != event.eventGuid) {
        newList.add(e);
      }
    }
    kEvents.update(event.day, (value) => newList);

    List<Event> newEvents = _selectedEvents.value
        .where((element) => element.eventGuid != event.eventGuid)
        .toList();
    _selectedEvents.value = newEvents;

    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SingleChildScrollView(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TableCalendar<Event>(
              firstDay: kFirstDay,
              lastDay: kLastDay,
              focusedDay: _focusedDay,
              selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
              calendarFormat: _calendarFormat,
              rangeSelectionMode: _rangeSelectionMode,
              eventLoader: _getEventsForDay,
              startingDayOfWeek: StartingDayOfWeek.sunday,
              daysOfWeekStyle: const DaysOfWeekStyle(
                  weekendStyle: TextStyle(color: Colors.red)),
              calendarStyle: const CalendarStyle(
                outsideDaysVisible: false,
                weekendTextStyle: TextStyle(color: Colors.red),
              ),
              onDaySelected: _onDaySelected,
              onFormatChanged: (format) {
                if (_calendarFormat != format) {
                  setState(() {
                    _calendarFormat = format;
                  });
                }
              },
              onPageChanged: (focusedDay) {
                _focusedDay = focusedDay;
              },
            ),
            const SizedBox(height: 8.0),
            Flexible(
              child: ValueListenableBuilder<List<Event>>(
                valueListenable: _selectedEvents,
                builder: (context, value, _) {
                  return ListView.builder(
                    shrinkWrap: true,
                    itemCount: value.length,
                    itemBuilder: (context, index) {
                      return Container(
                        margin: const EdgeInsets.symmetric(
                          horizontal: 12.0,
                          vertical: 4.0,
                        ),
                        decoration: BoxDecoration(
                          border: Border.all(),
                          borderRadius: BorderRadius.circular(12.0),
                        ),
                        child: ListTile(
                          onTap: () => print('${value[index]}'),
                          title: Text('${value[index]}'),
                          trailing: IconButton(
                              onPressed: () =>
                                  _removeEvent(index, value[index]),
                              icon: const Icon(Icons.delete)),
                        ),
                      );
                    },
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

4-3. lib\main.dart

import 'package:flutter/material.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'pages/example.dart';

void main() {
  initializeDateFormatting().then((_) => runApp(const MyApp()));
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'TableCalendar Example'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: const Center(child: TableExample()),
    );
  }
}

5. 結果

6. 備考

table_calendarを使用してカレンダーを表示する内容でした。

7. 参考

投稿者プロフィール

Sondon
開発好きなシステムエンジニアです。
卓球にハマってます。

関連記事

  1. flutter

    【Flutter】コード自動生成とGraphQLClientを使ってG…

  2. flutter

    【Flutter】Cloud Firestoreと連携

  3. flutter

    【Flutter】StateNotifierProviderで状態管理…

  4. 【Widget of the Week】#5 Opacity

  5. flutter

    【Flutter】GoogleMapを使用したアプリの開発について P…

  6. flutter

    【Flutter】Pankoに「みんなで割り勘」機能を追加

最近の記事

  1. AWS
  2. flutter

制作実績一覧

  1. Checkeys