DataStore for modern preferences

Alex Chen Jan 2026
2 tabs
package com.example.myapp.data

import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import java.io.IOException
import javax.inject.Inject
import javax.inject.Singleton

private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
    name = "user_preferences"
)

@Singleton
class UserPreferencesRepository @Inject constructor(
    private val context: Context
) {
    private val dataStore = context.dataStore

    companion object {
        val THEME_MODE = intPreferencesKey("theme_mode")
        val NOTIFICATIONS_ENABLED = booleanPreferencesKey("notifications_enabled")
        val FONT_SIZE = floatPreferencesKey("font_size")
        val USERNAME = stringPreferencesKey("username")
        val LAST_SYNC_TIME = longPreferencesKey("last_sync_time")
    }

    val userPreferencesFlow: Flow<UserPreferences> = dataStore.data
        .catch { exception ->
            if (exception is IOException) {
                emit(emptyPreferences())
            } else {
                throw exception
            }
        }
        .map { preferences ->
            UserPreferences(
                themeMode = ThemeMode.values()[
                    preferences[THEME_MODE] ?: ThemeMode.SYSTEM.ordinal
                ],
                notificationsEnabled = preferences[NOTIFICATIONS_ENABLED] ?: true,
                fontSize = preferences[FONT_SIZE] ?: 14f,
                username = preferences[USERNAME],
                lastSyncTime = preferences[LAST_SYNC_TIME] ?: 0L
            )
        }

    suspend fun updateThemeMode(themeMode: ThemeMode) {
        dataStore.edit { preferences ->
            preferences[THEME_MODE] = themeMode.ordinal
        }
    }

    suspend fun updateNotificationsEnabled(enabled: Boolean) {
        dataStore.edit { preferences ->
            preferences[NOTIFICATIONS_ENABLED] = enabled
        }
    }

    suspend fun updateFontSize(size: Float) {
        dataStore.edit { preferences ->
            preferences[FONT_SIZE] = size
        }
    }

    suspend fun updateUsername(username: String) {
        dataStore.edit { preferences ->
            preferences[USERNAME] = username
        }
    }

    suspend fun updateLastSyncTime(timestamp: Long) {
        dataStore.edit { preferences ->
            preferences[LAST_SYNC_TIME] = timestamp
        }
    }

    suspend fun clearAll() {
        dataStore.edit { preferences ->
            preferences.clear()
        }
    }
}

data class UserPreferences(
    val themeMode: ThemeMode = ThemeMode.SYSTEM,
    val notificationsEnabled: Boolean = true,
    val fontSize: Float = 14f,
    val username: String? = null,
    val lastSyncTime: Long = 0L
)

enum class ThemeMode {
    LIGHT, DARK, SYSTEM
}
2 files · kotlin Explain with highlit

DataStore replaces SharedPreferences with coroutine and Flow support. Preferences DataStore stores key-value pairs with type safety using preferencesDataStore delegate. Proto DataStore stores typed objects using Protocol Buffers. I use dataStore.data Flow to observe changes reactively. edit() lambda performs transactional updates. DataStore handles file I/O asynchronously on Dispatchers.IO, preventing ANR. Error handling uses try/catch or catch operators on Flows. Migrations import SharedPreferences data. The API is fully async unlike SharedPreferences' synchronous operations. DataStore guarantees data consistency and supports structured concurrency. It's the recommended modern alternative for Android app settings.