Flutter is a powerful framework for building mobile applications and comes to the rescue with its flexibility and ease of use. In this article, we will explore how to create a Birthday Reminder App in Flutter, so that next time, we don’t forget the birthday of any friend. A sample video is given below to get an idea about what we are going to do in this article.
Step-by-Step Implementation
Step 1: Create a Project
In the Visual Studio Code terminal run the below command to create a Flutter project:
flutter create birthday_reminder_app |
Step 2: Add packages
Now, add the table_calendar package into the pubspec.yaml file, and press Ctrl+S in order to install the package.
Step 3: Add images
Let’s add assets folder under assets section of pubspec.yaml file, so that we can use images we are going to use in app as shown in below image:
Step 4: Create Birthday Class
In birthday_model.dart file, create Birthday class
class Birthday {
final String name;
final DateTime date;
Birthday({required this .name, required this .date});
} |
Step 5: Create utils.dart
Create the Event class to store event titles, and key maps for storing events on selected dates.
import 'dart:collection' ;
import 'package:table_calendar/table_calendar.dart' ;
class Event {
final String title;
const Event( this .title);
@override
String toString() => title;
} var kEvents = LinkedHashMap<DateTime, List<Event>>( equals: isSameDay,
hashCode: getHashCode,
)..addAll(kEventSource); var kEventSource = LinkedHashMap<DateTime, List<Event>>(); int getHashCode(DateTime key) {
return key.day * 1000000 + key.month * 10000 + key.year;
} List<DateTime> daysInRange(DateTime first, DateTime last) { final dayCount = last.difference(first).inDays + 1;
return List.generate(
dayCount,
(index) => DateTime.utc(first.year, first.month, first.day + index),
);
} final kToday = DateTime.now(); final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day); final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day); |
Step 6: Create events_table.dart
Create EventsTable widget that implements a calendar interface with event management, so that users can view birthdays added as events in the calendar. The events are fetched based on the selected day and displayed in a scrollable list below the calendar.
import 'package:flutter/material.dart' ;
import 'package:table_calendar/table_calendar.dart' ;
import '../utils.dart' ;
class EventsTable extends StatefulWidget {
@override
_EventsTableExampleState createState() => _EventsTableExampleState();
} class _EventsTableExampleState extends State<EventsTable> {
late final ValueNotifier<List<Event>> _selectedEvents;
CalendarFormat _calendarFormat = CalendarFormat.month;
RangeSelectionMode _rangeSelectionMode = RangeSelectionMode.toggledOff;
DateTime _focusedDay = DateTime.now();
DateTime? _selectedDay;
@override
void initState() {
super.initState();
_selectedDay = _focusedDay;
_selectedEvents = ValueNotifier(_getEventsForDay(_selectedDay!));
}
@override
void dispose() {
_selectedEvents.dispose();
super.dispose();
}
List<Event> _getEventsForDay(DateTime day) {
return kEvents[day] ?? [];
}
List<Event> _getEventsForRange(DateTime start, DateTime end) {
final days = daysInRange(start, end);
return [
for (final d in days) ..._getEventsForDay(d),
];
}
void _onDaySelected(DateTime selectedDay, DateTime focusedDay) {
if (!isSameDay(_selectedDay, selectedDay)) {
setState(() {
_selectedDay = selectedDay;
_focusedDay = focusedDay;
_rangeSelectionMode = RangeSelectionMode.toggledOff;
});
_selectedEvents.value = _getEventsForDay(selectedDay);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text( 'Events' ),
),
body: Column(
children: [
TableCalendar<Event>(
firstDay: kFirstDay,
lastDay: kLastDay,
focusedDay: _focusedDay,
selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
calendarFormat: _calendarFormat,
rangeSelectionMode: _rangeSelectionMode,
eventLoader: _getEventsForDay,
startingDayOfWeek: StartingDayOfWeek.monday,
calendarStyle: CalendarStyle(
outsideDaysVisible: false ,
),
onDaySelected: _onDaySelected,
onFormatChanged: (format) {
if (_calendarFormat != format) {
setState(() {
_calendarFormat = format;
});
}
},
onPageChanged: (focusedDay) {
_focusedDay = focusedDay;
},
),
const SizedBox(height: 8.0),
Expanded(
child: ValueListenableBuilder<List<Event>>(
valueListenable: _selectedEvents,
builder: (context, value, _) {
return ListView.builder(
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]}' ),
),
);
},
);
},
),
),
],
),
);
}
} |
Step 7: Create birthday page
In main.dart, create birthday page to add birthdays in a AlertDialog when floating button is clicked and show added birthdays using ListTile.
import 'package:birthdayreminderapp/birthday_model.dart' ;
import 'package:flutter/material.dart' ;
import '../utils.dart' ;
import 'events_table.dart' ;
void main() {
runApp( const MyApp());
} class MyApp extends StatelessWidget {
const MyApp({Key? key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false ,
title: 'Birthday App' ,
theme: ThemeData(
primarySwatch: Colors.green
),
home: BirthdayReminderApp(),
);
}
} class BirthdayReminderApp extends StatefulWidget {
@override
_BirthdayReminderAppState createState() => _BirthdayReminderAppState();
} class _BirthdayReminderAppState extends State<BirthdayReminderApp> {
TextEditingController nameController = TextEditingController();
TextEditingController dateController = TextEditingController();
List<Birthday> birthdays = [];
DateTime selectedDate = DateTime.now();
List<Event> eventList = [];
var event;
late int date = 1;
late int month = 1;
late int year = 2023;
_showSnackBar(message){
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.green
)
); }
Future< void > _selectDate(BuildContext context) async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != selectedDate) {
setState(() {
selectedDate = pickedDate;
dateController.text = selectedDate.toString();
});
}
}
setEvent(name, date) {
event = Event(name + " Birthday" );
if (kEventSource[date] == null) {
eventList = [event];
kEventSource
..addAll({
date: eventList,
});
setState(() {
kEvents = kEvents;
});
} else {
kEventSource[date]!.add(event);
setState(() {
kEvents..addAll(kEventSource);
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true ,
title: Text( 'Gfg Birthday Reminder' ),
),
body: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage( "assets/balloons.png" ), fit: BoxFit.cover),
),
child: ListView.builder(
itemCount: birthdays.length,
itemBuilder: (context, index) {
Birthday birthday = birthdays[index];
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey,
offset: const Offset(
1.0,1.0
),
blurRadius: 2.0,
spreadRadius: 2.0,
),
]
),
margin: EdgeInsets.all(10),
child: ListTile(
title: Text(birthday.name),
subtitle: Text(
'${birthday.date.day}/${birthday.date.month}/${birthday.date.year}' ),
trailing: const Icon(Icons.cake),
),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
nameController.text = "" ;
dateController.text = "" ;
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
scrollable: true ,
title: const Text( 'Add new Birthday????' ),
content: SizedBox(
height: 265,
child: Column(
children: [
Row(children: [
const Icon(Icons.person_2_rounded),
const SizedBox(width: 10),
Flexible(
child: TextField(
controller: nameController,
decoration: InputDecoration(labelText: 'Name' ),
),
)
]),
Row(
children: [
GestureDetector(
child: Icon(Icons.calendar_today),
onTap: () {
_selectDate(context);
},
),
const SizedBox(width: 10),
Flexible(
child: TextField(
controller: dateController,
decoration:
InputDecoration(labelText: 'Picked Date' ),
),
),
],
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
if (nameController.text.isEmpty ||
dateController.text.isEmpty) {
_showSnackBar( 'Make sure both name and date is provided.????' );
} else {
DateTime? parsedDate =
DateTime.tryParse(dateController.text);
if (parsedDate != null) {
Birthday newBirthday = Birthday(
name: nameController.text,
date: parsedDate);
setState(() {
birthdays.add(newBirthday);
});
Navigator.pop(context);
_showSnackBar( 'Birthday added????????' );
setEvent(nameController.text, parsedDate);
} else {
_showSnackBar( 'Invalid Date!' );
}
}
},
child: const Text( 'Add Birthday' ),
),
const SizedBox(height: 10),
ElevatedButton(
child: const Text( "View Events" ),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => EventsTable()),
);
},
)
],
),
),
);
},
);
},
tooltip: "Add Birthday" ,
child: const Icon(Icons.add),
),
);
}
} |