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. 参考
投稿者プロフィール
-
開発好きなシステムエンジニアです。
卓球にハマってます。