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.