• Labs icon Lab
  • Core Tech
Labs

Guided: Build a Habit Tracker in Kotlin

In this Guided Code Lab with Kotlin, you will build a Habit Tracker application. The objective is to develop a functional habit-tracking system, allowing users to add habits, mark them as completed, and view their progress. Your journey in this lab will provide practical insights into Kotlin fundamentals, data handling using data classes, user interaction through a console-based interface, object-oriented programming principles, and the compilation and testing of Kotlin code. By the end of the lab, you'll have a tangible understanding of Kotlin programming, enabling you to build simple yet effective applications.

Labs

Path Info

Level
Clock icon Intermediate
Duration
Clock icon 31m
Published
Clock icon Feb 08, 2024

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. Challenge

    Introduction

    Scenario

    Imagine you have a list of habits you want to track, such as drinking enough water, exercising daily, or reading for at least 30 minutes. However, keeping track of these habits manually can be tedious and prone to errors. That's where a Habit Tracker application comes in handy! With this application, you can add your habits, mark them as completed when you accomplish them, view your progress, and even delete habits you no longer wish to track.


    Code Lab

    In this Guided Code Lab, you will delve into the realm of Kotlin programming to create a Habit Tracker application from the ground up. Your focus will be primarily on the HabitTracker.kt file, where you'll implement essential functions crucial for constructing the Habit Tracker.

    Completing this Kotlin Habit Tracker lab will involve mastering the following key concepts:

    Kotlin Basics: Gain proficiency in fundamental Kotlin syntax and language features.

    Data Handling: Learn to model structured data using Kotlin data classes. Implement date handling using java.time.LocalDate.

    User Interaction: Develop a console-based user interface for habit tracking. Manage user input and execute corresponding actions.

    Before you begin, here are some key points:

    1. Your task involves implementing code in the Habit.kt and HabitTracker.kt files.
    2. If you encounter any challenges along the way, feel free to consult the solution directory.
    3. To simplify the process, comments are included to help you find the necessary changes for each task according to the step you're working on.
      For instance, if you're currently on Step 3, you can locate the relevant changes by finding // T0-DO Step 3 in the file.
  2. Challenge

    Define the Habit Data Class

    To build your Habit Tracker, you will need to understand data classes and learn how to leverage them to efficiently store and manage data. Your goal is to create a data class to represent habit data. Get started by understanding what a data class is and how it can simplify the process of storing your habit-related information.


    ### Data Class

    In Kotlin, a data class is a special class designed to hold data in a concise and effective way. It comes with built-in functionality, such as automatic generation of common methods like toString(), equals(), and hashCode(). This makes it an ideal choice for modeling simple data structures where the focus is on the data itself.

    Consider an example where you have a Task data class with a MutableList property to represent a list of subtasks:

    data class Task(val title: String, val subtasks: MutableList<String>)
    

    You'll utilize the features of data classes to streamline the creation and management of habit data.

    Next, you will create a data class named Habit to represent information about habits. A habit typically has a name, startDate, and a list of completedDates.

    Great job! Next, you will understand about the HabitTracker class and build a CLI Menu.

  3. Challenge

    Explore the `HabitTracker` Class & Construct the Menu

    Now review the current setup and the files which you will be working with.

    The Main.kt File

    This file acts as an entry point, instantiating the HabitTracker class and calling the start() function of HabitTracker class.

    fun main() {
        val habitTracker = HabitTracker()
        habitTracker.start()
    }
    

    The HabitTracker.kt File

    This file contains the code for user interactions and habit management.

    Private Variables

    Within the HabitTracker class, you have two private properties:

    • habits: This is a mutable list which will be used to store habits.

    • scanner: This is an instance of Scanner for handling user input in your program.

    class HabitTracker {
        private val habits = mutableListOf<Habit>()
        private val scanner = Scanner(System.`in`)
    

    The start Function

    This function provides users with a Menu Interface and captures the user inputs. Inside the while loop , the application continuously displays a menu to the user. You will use the when construct to create a switch-case statement.

    The when Expression

    In Kotlin, the when expression serves as a more powerful and flexible alternative to traditional switch-case statements found in other programming languages. It allows you to match a value against multiple cases and execute different code blocks based on the matching case.

    Example :

    println("Please enter a number (1-3): ")
            when (scanner.nextInt()) {
            1 -> println("You entered 1")
            2 -> println("You entered 2")
            3 -> println("You entered 3")
            else -> println("Invalid Input")
        }
    

    In the above example, the else option in a when expression acts as the default case, executing when none of the other option match. This helps handle unexpected inputs or conditions gracefully.


    Now you will learn about the available options and their respective methods for the Habit Tracker :

    1. Add Habit

    addHabit(): This function will prompt the user to input details about a new habit they want to track. It will then add this habit to the list of habits stored within the HabitTracker instance.

    2. Mark Habit Completed

    markHabitCompleted(): This function will display a list of habits that the user can mark as completed. The user will select a habit from the list, and the function will mark it as completed.

    3. View Progress

    viewProgress(): This function offers users an insight into their habit-tracking journey. It presents a summary of habits along with the count of days each habit has been completed since its inception.

    4. Delete Habit

    deleteHabit() : This function will allow users to remove a habit from their list of tracked habits. It will prompt the user to select the habit they want to delete and then remove it from the list.

    5. Exit

    This option exits the Habit Tracker gracefully.


    Now you will design the Menu for the Habit Tracker. In the next steps, you are going to implement the habit tracker functions in order to have a fully functioning Habit Tracker.

  4. Challenge

    Implement Add Habit

    In this step, you will implement the addHabit method within the HabitTracker class. This method prompts you to enter the name of a new habit, creates a habit instance with the current date and an empty list of completed dates, and adds the habit to the list.

    You are going to use Scanner in order to get the user input. Next, you will learn about Scanners.

    Scanner

    Scanners allow you to read input from the standard input stream (System.in). You can use them to retrieve various types of data, such as strings, numbers, or entire lines of text, entered by the user during program execution.

    Some commonly used methods include:

    • next(): Reads the next token (a word) from the input.
    • nextInt(): Reads the next token as an Int.
    • nextLine(): Reads the next line of input as a string.

    Here is an example:

        val name = scanner.next()
    

    In the above example the user input from the console will be assigned to variable name.

    The function addHabit() is responsible for allowing users to add a new habit to the Habit Tracker application. Now, you will complete the implementation of this method. In this step, you have learned how to use scanner to get the console input, create an object, and insert it into a mutable list.

    Next, you're going to implement the markHabitCompleted method, which will allow you to add tracking information to a habit regarding it's completion.

  5. Challenge

    Implement Mark Habit Completed

    In this step, you will implement the markHabitCompleted method within the HabitTracker class. This method enables users to mark a habit as completed for the current date.


    Before you start the implementation, you will need to understand how you can retrieve an item from the list.

    The Indexing operator [] operator helps to retrieve an element at a specific position within a collection, such as arrays, lists, and maps, using their indices or keys.

    Example:

    val element = list[index]
    

    Here, index represents the position of the element you want to access in the list. The indexing operator allows you to specify this position within the square brackets and the element at that position is returned.

    Similarly in maps, the indexing operator [] is used to access values based on their keys.

    val myMap = mapOf("name" to "John", "age" to 30)
    println(myMap["name"]) // Output: "John"
    

    You will now explore the isEmpty() method as well.
    In Kotlin, isEmpty() is a method accessible on collections like lists, sets, and maps. It enables you to determine whether the collection holds any elements or if it's empty.

    Example:

    val myList = listOf<Int>()
    println(myList.isEmpty()) // Output: true
    

    Now you will implement the markHabitCompleted method which provides users with the ability to mark a habit as completed, ensuring accurate tracking of their progress within the Habit Tracker application. In this step, you have learned about the indexing operator [] and isEmpty() method available in collections.

    Next you're going to implement the viewProgress method which allows you to keep track of the all the habits that you're following and see the progress of each habit.

  6. Challenge

    Implement viewProgress Method

    In this step, you are going to complete the viewProgress method within the HabitTracker class. If there are existing habits, the program prints the progress of each habit, indicating the number of completed days out of the total days since the habit's start.


    Next, you will learn how you iterate over a list using the forEach loop.

    The forEach Loop

    The forEach loop in Kotlin provides a convenient way to iterate over elements in a collection and perform specific actions for each element.

    Example:

    collection.forEach { element ->
        // Code to be executed for each element
    		println("Element Value ${element}") 
    }
    

    For each element in the collection, the lambda expression { element -> println("Element Value ${element}") } is executed. The current element is represented by the parameter element within the lambda expression.



    Now, you will understand the code of viewProgress function.

    The Habit object comprises variables such as name, startDate, and completedDate, all of which are stored within a list called habits.

    The subsequent code snippet is responsible for printing the habit's name alongside the number of days it has been completed since its initiation:

    habits.forEach { habit ->
    println("${habit.name}: ${habit.completedDates.size} completed out of ${calculateDaysSinceStart(habit)} days")
    }
    

    Here's what happens within this snippet:

    • completedDates.size yields the count of days the habit has been marked as completed.

    • calculateDaysSinceStart method computes the number of days since the habit commenced.


    The calculateDaysSinceStart Function

    This function calculates the number of days it has been since the habit has started.

    Consider a simple scenario: To determine the duration of a habit, suppose you commenced the habit on the 10th Jan 2024 and the current date is the 14th Jan 2024.

    days = 14 - ( 10 + 1 ) = 5 days ( 10th , 11th, 12th , 13th, 14th ) 
    

    The addition of 1 to the sum is necessary to incorporate the present day into the calculation.

    This logic will work if your start date and end date are in same month. Since the habit can span multiple months, you will need to use better logic to implement your calculation.

    Next, you will understand about LocalDate class which will be helpful for implementing the calculateDaysSinceStart method.

    The LocalDate.now() Function

    In Kotlin, LocalDate.now() is a function call that returns the current date in the local time zone. It retrieves the date without the time component, representing the current day, month, and year.

    Following that, .toEpochDay() is a method used to convert a LocalDate object to the number of days since January 1, 1970 (the Unix epoch). It returns a long value representing the number of days elapsed since that reference date.

    Together, LocalDate.now().toEpochDay() provides a convenient way to obtain the current date and convert it into a standardized representation of days since the Unix epoch.

    Here you will perform the calculation again using Epoch date ( January 1, 1970 ) as a reference:

    days = (Epoch date to 14th Jan) - (Epoch date to 10th Jan) + 1
    days = 19737 - 19733 + 1
    days = 5
    

    This logic will work to find days between multiple months.


    Now, you will use this understanding to complete your calculateDaysSinceStart method and get the viewProgress method working.

    To calculate the calculateDaysSinceStart, you will need to subtract the present date epoch from the epoch date since the habit has started. In this step, you have learned about toEpochDay() which helps to convert a LocalDate object into the number of days since the epoch.

    Next, you'll be tasked with implementing the deleteHabit method, enabling you to remove habits that are no longer of interest to you.

  7. Challenge

    Implement deleteHabit Method

    There is a possibility that you no longer want to pursue a certain habit. In this step, you are going to add support for habit deletion.

    In Kotlin, the removeAt(index) function is used to remove an element from a mutable list at a specific index.

    When you call removeAt(index) on a mutable list, Kotlin removes the element at the specified index and shifts all subsequent elements to the left to fill the gap created by the removal. It also returns the removed item.

    Here is an example :

    val removedElement = xyzList.removeAt(2)
    

    Now you will finish the implementation of deleteHabit() function. Great! Now that you've successfully completed all steps you can test the Habit Tracker by clicking on the Run button on the bottom right of the Terminal tab.

    Alternatively you can type java -jar out.jar in the Terminal to run the application.

  8. Challenge

    Conclusion and Next Steps

    Congratulations on creating a working Habit Tracker application!

    Throughout this course, you have learned valuable concepts and techniques in Kotlin programming while building a practical habit tracker application. You've gained insights into data classes, user input handling, list manipulation, and more, all of which are fundamental skills in Kotlin development.

    Next you can improve the habit tracker with the following :

    • Goal setting : Enable users to set specific goals for each habit, such as a target number of repetitions per day or week, and track their progress towards these goals.
    • Streak Tracking : Implement a streak feature to track how many consecutive days users have successfully completed a habit. Streaks can be motivating and encourage users to maintain consistency.
    • Custom Habit Categories: Allow users to organize their habits into custom categories or tags, such as health, fitness, productivity, etc., to better manage and prioritize their goals.

    You can continue with your learning journey by going through the following courses on Pluralsight:

    Keep exploring, experimenting, and honing your Kotlin skills. With dedication and practice, you'll continue to grow as a proficient Kotlin developer. Best wishes on your coding endeavors!

Amar Sonwani is a software architect with more than twelve years of experience. He has worked extensively in the financial industry and has expertise in building scalable applications.

What's a lab?

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Provided environment for hands-on practice

We will provide the credentials and environment necessary for you to practice right within your browser.

Guided walkthrough

Follow along with the author’s guided walkthrough and build something new in your provided environment!

Did you know?

On average, you retain 75% more of your learning if you get time for practice.