【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】多言語化,Internationalization

  2. flutter

    【Flutter】Cloud Firestoreと連携

  3. flutter

    【Flutter】Pankoに「どこでもあみだくじ」機能を追加

  4. flutter

    【Flutter】FlutterとKotlinでそれぞれCounter…

  5. flutter

    【Flutter】GraphQLClientを使ってGraphQL・A…

  6. flutter

    【Flutter】Widgetテスト(Flutter,VSCode,T…

最近の記事

  1. AWS
  2. AWS
  3. AWS

制作実績一覧

  1. Checkeys