I'm developing a Flutter calendar app that enables users to add events to specific dates. The calendar effectively displays events as markers on the dates, and tapping on a date should display the corresponding events in a list view beneath the calendar. However, I'm encountering an issue: when I tap off a date and then return to it, the events that were previously shown in the list view disappear. The events are being saved in a map so there is probably an issue with how teh program is outputting them
I use the EventList class to output the events:
class EventList extends StatelessWidget {
final List<Event> events;
const EventList({
required this.events,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
if (events.isEmpty) {
return Container(
padding: const EdgeInsets.all(16.0),
child: const Text(
'No Events',
style: TextStyle(color: Colors.grey),
),
);
} else {
return Expanded(
child: ListView.builder(
itemCount: events.length,
itemBuilder: (context, index) {
final event = events[index];
return Container(
margin:
const EdgeInsets.symmetric(vertical: 4.0, horizontal: 12.0),
decoration: const BoxDecoration(
color: Colors.white,
border: Border(bottom: BorderSide(color: Colors.grey)),
),
child: ListTile(
onTap: () => print(""),
title: Text(event.title),
),
);
},
),
);
}
}
}
Code calling the class:
List<Event> _getEventsForDay(DateTime day) {
return events[day] ?? [];
}
EventList(
events: _getEventsForDay(_selectedDay!),
),
The events look like they are being saved in the map but I don't really know what the error is. Here is the full code
// ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart';
import 'package:table_calendar/table_calendar.dart';
class Event {
final String title;
final DateTime dateTime;
Event(this.title, this.dateTime);
}
void main() {
runApp(const MaterialApp(
home: Calendar(),
));
}
class Calendar extends StatefulWidget {
const Calendar({Key? key}) : super(key: key);
@override
State<Calendar> createState() => _CalendarState();
}
class _CalendarState extends State<Calendar> {
final DateTime _focusedDay = DateTime.now();
DateTime? _selectedDay;
Map<DateTime, List<Event>> events = {};
final TextEditingController _eventController = TextEditingController();
@override
void initState() {
super.initState();
_selectedDay = _focusedDay;
}
List<Event> _getEventsForDay(DateTime day) {
return events[day] ?? [];
}
void _showEventInputDialog() async {
final selectedDate = await showDatePicker(
context: context,
initialDate: _focusedDay,
firstDate: DateTime(2020),
lastDate: DateTime(2030),
);
if (selectedDate != null) {
setState(() {
_selectedDay = selectedDate;
});
showModalBottomSheet(
context: context,
builder: (context) {
return Container(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Add Event - ${_selectedDay!.toString().substring(0, 10)}',
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 16.0),
TextField(
controller: _eventController,
decoration: const InputDecoration(hintText: 'Event Name'),
),
const SizedBox(height: 16.0),
ElevatedButton(
onPressed: () {
if (_eventController.text.trim().isNotEmpty) {
setState(() {
final newEvent = Event(
_eventController.text.trim(),
_selectedDay!,
);
if (events[_selectedDay!] == null) {
events[_selectedDay!] = [newEvent];
} else {
events[_selectedDay!]!.add(newEvent);
}
_eventController.clear();
});
Navigator.pop(context);
}
},
child: const Text('Add Event'),
),
],
),
);
},
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Calendar'),
),
floatingActionButton: FloatingActionButton(
onPressed: _showEventInputDialog,
child: const Icon(Icons.add),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TableCalendar(
calendarBuilders: CalendarBuilders(
markerBuilder: (context, date, events) {
if (events.isNotEmpty) {
return Positioned(
bottom: 0,
child: Container(
width: 6,
height: 6,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
),
),
);
}
return const SizedBox();
},
),
availableGestures: AvailableGestures.all,
selectedDayPredicate: (day) {
return isSameDay(_selectedDay, day);
},
focusedDay: _focusedDay,
firstDay: DateTime.utc(2020, 01, 01),
lastDay: DateTime.utc(2030, 01, 01),
onDaySelected: (selectedDay, focusedDay) {
setState(() {
_selectedDay = selectedDay;
});
},
eventLoader: _getEventsForDay,
),
const SizedBox(height: 8.0),
const Divider(
indent: 4,
endIndent: 16,
thickness: 1,
color: Colors.grey,
),
EventList(
events: _getEventsForDay(_selectedDay!),
),
],
),
);
}
}
class EventList extends StatelessWidget {
final List<Event> events;
const EventList({
required this.events,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
if (events.isEmpty) {
return Container(
padding: const EdgeInsets.all(16.0),
child: const Text(
'No Events',
style: TextStyle(color: Colors.grey),
),
);
} else {
return Expanded(
child: ListView.builder(
itemCount: events.length,
itemBuilder: (context, index) {
final event = events[index];
return Container(
margin:
const EdgeInsets.symmetric(vertical: 4.0, horizontal: 12.0),
decoration: const BoxDecoration(
color: Colors.white,
border: Border(bottom: BorderSide(color: Colors.grey)),
),
child: ListTile(
onTap: () => print(""),
title: Text(event.title),
),
);
},
),
);
}
}
}
It's because when you add event in map the selected day value is: (2023-08-30 00:00:00.000)
And when you tap on a day it gives : (2023-08-30 00:00:00.000Z)
just update your onDaySelected:
onDaySelected: (selectedDay, focusedDay) {
setState(() {
_selectedDay = DateTime.parse(
DateFormat('yyyy-MM-dd HH:mm:ss.SSS').format(selectedDay));
});
},