Implementing Shared Preferences in Android Using Kotlin

Learn how to implement Shared Preferences in Android using Kotlin with our step-by-step guide. Master efficient data storage for your Android apps.

Shared Preferences in Android Using Kotlin

Welcome back, fellow Android app developers! In today’s blog post, we’re diving deep into the realm of data storage in Android applications, specifically focusing on Shared Preferences and how to store various types of data using Kotlin.

By the end of this tutorial, you’ll have a solid understanding of how to efficiently manage different data types in Shared Preferences and implement them in a real-world example project.

Understanding Shared Preferences

Shared Preferences is a key-value pair storage system provided by Android for persisting simple data types. It’s commonly used to store user preferences, settings, and small amounts of data that need to be quickly accessed.

In this tutorial, we’ll explore how to utilize Shared Preferences to store and retrieve different data types such as strings, integers, booleans, and sets.

Setting Up the Example Project

Let’s kick things off by setting up a simple Android project. Ensure you have the latest version of Android Studio installed and create a new project with an empty activity.

Project Setup

Open Android Studio, click on “Start a new Android Studio project,” and follow the wizard. Choose an empty activity to keep things minimal.

Define the DataModel Class

Create a Kotlin data class that represents the data structure you want to store in Shared Preferences. For our example, let’s create a UserSettings data class.

package com.example.sharedpref

data class UserData(
    val username: String,
    val age: Int,
    val isPremiumUser: Boolean,
    val favoriteSubjects: Set<String>
)

Implement Shared Preferences Manager

Create a SharedPreferencesManager class responsible for handling the storing and retrieving of data in Shared Preferences.

package com.example.sharedpref

import android.content.Context
import android.content.SharedPreferences

class SharedPrefManager(context: Context) {
    private val sharedPreferences: SharedPreferences =
        context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)

    fun saveUserData(userData: UserData) {
        with(sharedPreferences.edit()) {
            putString("username", userData.username)
            putInt("age", userData.age)
            putBoolean("isPremiumUser", userData.isPremiumUser)
            putStringSet("favoriteGenres", userData.favoriteSubjects)
            apply()
        }
    }

    fun getUserData(): UserData {
        return UserData(
            sharedPreferences.getString("username", "") ?: "",
            sharedPreferences.getInt("age", 0),
            sharedPreferences.getBoolean("isPremiumUser", false),
            sharedPreferences.getStringSet("favoriteGenres", setOf()) ?: setOf()
        )
    }
}

Storing and Retrieving

Create Layout

Navigate to the activity_main.xml file and replace the existing code with the following.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:title="Shared Preferences" />

    <com.google.android.material.divider.MaterialDivider
        android:id="@+id/divider"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/toolbar" />

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintTop_toBottomOf="@id/divider">

        <androidx.appcompat.widget.LinearLayoutCompat
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="16dp">

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:hint="Username">

 <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/username"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="textPersonName" />
           </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:hint="Age">
            <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/age"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="number"
                    android:maxLength="3" />
           </com.google.android.material.textfield.TextInputLayout>
           <com.google.android.material.materialswitch.MaterialSwitch
                android:id="@+id/premium"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:text="Premium subscriptions" />

            <com.google.android.material.chip.ChipGroup
                android:id="@+id/subject_list"
                style="@style/Widget.Material3.Chip.Filter"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                app:singleSelection="false">

                <com.google.android.material.chip.Chip
                    android:id="@+id/kotlin"
                    style="@style/Widget.Material3.Chip.Filter"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Kotlin" />

                <com.google.android.material.chip.Chip
                    android:id="@+id/python"
                    style="@style/Widget.Material3.Chip.Filter"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Python" />

                <com.google.android.material.chip.Chip
                    android:id="@+id/android"
                    style="@style/Widget.Material3.Chip.Filter"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Android" />
            </com.google.android.material.chip.ChipGroup>

            <Button
                android:id="@+id/save_data"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="8dp"
                android:text="Save profile" />

            <com.google.android.material.card.MaterialCardView
                style="@style/Widget.Material3.CardView.Filled"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="8dp">

                <androidx.constraintlayout.widget.ConstraintLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical"
                    android:padding="16dp">

                    <TextView
                        android:id="@+id/username_load"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:paddingHorizontal="8dp"
                        android:text="Username"
                        android:textAppearance="?attr/textAppearanceTitleLarge"
                        app:layout_constraintTop_toTopOf="parent" />

                    <TextView
                        android:id="@+id/age_load"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_margin="8dp"
                        android:text="Age"
                        android:textAppearance="?attr/textAppearanceTitleMedium"                    app:layout_constraintStart_toStartOf="@id/username_load"                      app:layout_constraintTop_toBottomOf="@id/username_load" />

                    <TextView
                        android:id="@+id/premium_load"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_margin="8dp"
                        android:text="Premium"
                        android:textAppearance="?attr/textAppearanceTitleMedium"
app:layout_constraintStart_toEndOf="@id/age_load"           app:layout_constraintTop_toBottomOf="@id/username_load" />

                    <com.google.android.material.chip.ChipGroup
                        android:id="@+id/subject_list_load"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_margin="8dp"
app:layout_constraintTop_toBottomOf="@id/age_load" />
                </androidx.constraintlayout.widget.ConstraintLayout>
            </com.google.android.material.card.MaterialCardView>
        </androidx.appcompat.widget.LinearLayoutCompat>
    </androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

Store and Retrieve Data

Navigate to the MainActivity.kt class and replace the existing code with the following.

package com.example.sharedpref

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import com.example.sharedpref.databinding.ActivityMainBinding
import com.google.android.material.chip.Chip

class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var sharedPref: SharedPrefManager
    private val favoriteSubjects = mutableSetOf<String>()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        sharedPref = SharedPrefManager(this)

        binding.subjectList.setOnCheckedStateChangeListener { group, _ ->
            for (index in 0 until group.childCount) {
                val chip = group.getChildAt(index) as Chip
                if (chip.isChecked) {
                    favoriteSubjects.add(chip.text.toString())
                }
            }
        }

        // Storing and Retrieving Different Data Types
        binding.saveData.setOnClickListener {
            val username = binding.username.text.toString()
            val age = binding.age.text.toString().toInt()
            val premiumStatus = binding.premium.isChecked

            // Storing String, Integer, Boolean, and Set Data
            sharedPref.saveUserData(
                UserData(
                    username = username,
                    age = age,
                    isPremiumUser = premiumStatus,
                    favoriteSubjects = favoriteSubjects
                )
            )

            // Retrieving String, Integer, Boolean, and Set Data
            val userData = sharedPref.getUserData()
            binding.usernameLoad.text = userData.username
            binding.ageLoad.text = "${userData.age}Years"
            if (userData.isPremiumUser) {
                binding.premiumLoad.text = "Premium"
            } else {
                binding.premiumLoad.text = "Free"
            }
            val chipNames = userData.favoriteSubjects
            for (chipName in chipNames) {
                val chip = Chip(this)
                chip.text = chipName
                binding.subjectListLoad.addView(chip)
            }
        }
    }
}

Testing the App

Here is the final output. Launch the app, input all data, and click on the Save profile button.

Screenshot of an Android example project illustrating the implementation of Shared Preferences for data storage in Android development.
Shared Preferences in Android Demo

Conclusion

Congratulations! You’ve successfully learned how to store and retrieve different data types in Shared Preferences using Kotlin. This knowledge is crucial for building efficient and user-friendly Android applications.

Feel free to apply these techniques to your projects and explore further improvements, such as error handling and validation, to enhance the robustness of your data storage mechanisms. Happy coding!