Make a Calendar App with Flutter
What This App Does
A Calendar app serves a specific need in today's mobile-first world. Building one with Flutter means you ship to iOS, Android, and the web from a single codebase, cutting development time dramatically while keeping a native-quality experience.
Main Features
| Feature | Why It Matters |
|---|---|
| Month/Week/Day Views | Toggle between month grid, weekly agenda, and daily timeline |
| Event Creation | Tap a date to add an event with title, time, and colour-coded category |
| Recurrence Rules | Support daily, weekly, monthly, and custom repeat patterns |
How to Make a Calendar App with Flutter
Month/Week/Day Views
class CalendarScreen extends StatefulWidget {
@override State createState() => _CalendarScreenState();
}
class _CalendarScreenState extends State {
CalendarFormat _format = CalendarFormat.month;
DateTime _selectedDay = DateTime.now();
DateTime _focusedDay = DateTime.now();
@override
Widget build(BuildContext context) {
return Column(
children: [
TableCalendar(
firstDay: DateTime(2020),
lastDay: DateTime(2030),
focusedDay: _focusedDay,
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
calendarFormat: _format,
onFormatChanged: (format) => setState(() => _format = format),
onDaySelected: (selected, focused) {
setState(() {
_selectedDay = selected;
_focusedDay = focused;
});
},
),
Expanded(
child: EventList(date: _selectedDay),
),
],
);
}
}
Event Creation
class Event {
final String title;
final DateTime date;
final TimeOfDay time;
final Color color;
final String? recurrenceRule;
Event({required this.title, required this.date, required this.time,
required this.color, this.recurrenceRule});
}
class CreateEventSheet extends StatefulWidget {
final DateTime selectedDate;
const CreateEventSheet({super.key, required this.selectedDate});
@override State createState() => _CreateEventSheetState();
}
class _CreateEventSheetState extends State {
final _formKey = GlobalKey();
final _titleCtrl = TextEditingController();
TimeOfDay _time = TimeOfDay.now();
Color _color = Colors.blue;
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(
left: 16, right: 16, top: 16,
bottom: MediaQuery.of(context).viewInsets.bottom + 16),
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: _titleCtrl,
decoration: const InputDecoration(labelText: 'Event title'),
validator: (v) => v?.isEmpty == true ? 'Required' : null,
),
const SizedBox(height: 12),
ColorPicker(selected: _color, onChanged: (c) => setState(() => _color = c)),
const SizedBox(height: 12),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
Navigator.pop(context, Event(
title: _titleCtrl.text, date: widget.selectedDate,
time: _time, color: _color,
));
}
},
child: const Text('Save Event'),
),
],
),
),
);
}
}
Recommended Libraries
| Package | Purpose |
|---|---|
| table_calendar | Feature-rich calendar widget with multiple view formats |
| flutter_local_notifications | Schedule reminders for events |
| drift | Local database for event persistence and recurrence queries |
UI & UX Suggestions
Use a blue primary, white cards, category colour dots (red=work, green=personal, purple=health) colour palette to match the app's purpose. Keep the navigation simple — a bottom nav bar or a single-scroll layout works best for this type of app. Make sure every interaction provides haptic or visual feedback so the app feels responsive and polished.