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
}
package com.example.myapp.ui.settings
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.myapp.data.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class SettingsViewModel @Inject constructor(
private val preferencesRepository: UserPreferencesRepository
) : ViewModel() {
val userPreferences = preferencesRepository.userPreferencesFlow
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = UserPreferences()
)
fun updateThemeMode(themeMode: ThemeMode) {
viewModelScope.launch {
preferencesRepository.updateThemeMode(themeMode)
}
}
fun toggleNotifications(enabled: Boolean) {
viewModelScope.launch {
preferencesRepository.updateNotificationsEnabled(enabled)
}
}
fun updateFontSize(size: Float) {
viewModelScope.launch {
preferencesRepository.updateFontSize(size)
}
}
}
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.
Related snips
package com.example.myapp
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
Dependency injection with Hilt
package com.example.myapp.ui
import androidx.lifecycle.*
import kotlinx.coroutines.launch
class UserViewModel(
LiveData transformations and mediators
package com.example.myapp.utils
import android.os.Build
import android.os.StrictMode
import android.os.Trace
import timber.log.Timber
Performance optimization and profiling
package com.example.myapp.widget
import android.app.PendingIntent
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
App widgets for home screen
package com.example.myapp.data.repository
import com.example.myapp.data.local.PostDao
import com.example.myapp.data.local.PostEntity
import com.example.myapp.data.remote.ApiService
import com.example.myapp.models.Post
Unit testing with JUnit and MockK
package com.example.myapp.utils
import android.app.NotificationChannel
import android.app.NotificationChannelGroup
import android.app.NotificationManager
import android.app.PendingIntent
Notification channels and categories
Share this code
Here's the card — post it anywhere.