Open In App

Flutter – Build Birthday Reminder App

Last Updated : 06 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

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:

Dart




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.

tempsnip

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:

asset

Step 4: Create Birthday Class

In birthday_model.dart file, create Birthday class

Dart




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.

Dart




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.

Dart




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.

Dart




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),
        ),
    );
  }
}


Output:



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads