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. 参考
投稿者プロフィール

-
開発好きなシステムエンジニアです。
卓球にハマってます。
最新の投稿
【Next.js】2025年2月9日【NextJS】View and Download PDF
【AWS】2025年2月1日【AWS】Github ActionsやAWS SAMを使ってAWS S3・CloudFrontにウェブコンテンツをデプロイし、サブドメインにアクセスできるようにする
【AWS】2025年1月25日【AWS】Deploy Serverless NextJS app with AWS Lambda Web Adapter using AWS SAM
【Next.js】2025年1月16日【NextJS】Access nextjs app launched on WSL2 from an external terminal