Android DataStore Guide — Migration from SharedPreferences
DataStore is the modern replacement for SharedPreferences, offering better performance and type safety with coroutines.
Setup with Preferences DataStore
// Gradle dependency
dependencies {
implementation "androidx.datastore:datastore-preferences:1.0.0"
}
// Create extension function
val Context.settingsDataStore by preferencesDataStore(name = "settings")
// Type-safe key definitions
val IS_DARK_MODE = booleanPreferencesKey("is_dark_mode")
val USER_NAME = stringPreferencesKey("user_name")
val LAST_SYNC_TIME = longPreferencesKey("last_sync_time")
val THEME_COLOR = intPreferencesKey("theme_color")
val TAG_LIST = stringSetPreferencesKey("tags")
Repository Pattern with Flow
class SettingsRepository(private val context: Context) {
fun getUserNameFlow(): Flow<String> =
context.settingsDataStore.data.map { preferences ->
preferences[USER_NAME] ?: ""
}
fun getIsDarkModeFlow(): Flow<Boolean> =
context.settingsDataStore.data.map { preferences ->
preferences[IS_DARK_MODE] ?: false
}
suspend fun setUserName(name: String) {
context.settingsDataStore.edit { preferences ->
preferences[USER_NAME] = name
}
}
suspend fun setIsDarkMode(enabled: Boolean) {
context.settingsDataStore.edit { preferences ->
preferences[IS_DARK_MODE] = enabled
}
}
suspend fun updateLastSyncTime() {
context.settingsDataStore.edit { preferences ->
preferences[LAST_SYNC_TIME] = System.currentTimeMillis()
}
}
suspend fun clearAllSettings() {
context.settingsDataStore.edit { it.clear() }
}
}
ViewModel Integration with stateIn
class SettingsViewModel(
private val repository: SettingsRepository
) : ViewModel() {
val userName: StateFlow<String> = repository
.getUserNameFlow()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = ""
)
val isDarkMode: StateFlow<Boolean> = repository
.getIsDarkModeFlow()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = false
)
fun updateUserName(name: String) {
viewModelScope.launch {
repository.setUserName(name)
}
}
fun toggleDarkMode() {
viewModelScope.launch {
val current = isDarkMode.value
repository.setIsDarkMode(!current)
}
}
}
Compose Settings UI
@Composable
fun SettingsScreen(viewModel: SettingsViewModel) {
val userName by viewModel.userName.collectAsState()
val isDarkMode by viewModel.isDarkMode.collectAsState()
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
Text("Settings", style = MaterialTheme.typography.headlineSmall)
Spacer(modifier = Modifier.height(16.dp))
// Text input
TextField(
value = userName,
onValueChange = viewModel::updateUserName,
label = { Text("User Name") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
// Toggle switch
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text("Dark Mode", modifier = Modifier.weight(1f))
Switch(
checked = isDarkMode,
onCheckedChange = { viewModel.toggleDarkMode() }
)
}
// Action button
Button(
onClick = { viewModel.clearSettings() },
modifier = Modifier.fillMaxWidth()
) {
Text("Clear All Settings")
}
}
}
SharedPreferences Migration
suspend fun migrateFromSharedPreferences(
context: Context,
repository: SettingsRepository
) {
val sharedPref = context.getSharedPreferences("old_prefs", Context.MODE_PRIVATE)
// Migrate each value
if (sharedPref.contains("user_name")) {
val name = sharedPref.getString("user_name", "")
repository.setUserName(name!!)
}
if (sharedPref.contains("is_dark_mode")) {
val darkMode = sharedPref.getBoolean("is_dark_mode", false)
repository.setIsDarkMode(darkMode)
}
// Clear old SharedPreferences (optional)
sharedPref.edit().clear().apply()
}
// Call during app startup
LaunchedEffect(Unit) {
migrateFromSharedPreferences(context, repository)
}
Key Takeaways
- preferencesDataStore delegate simplifies setup
- Use type-safe keys (booleanPreferencesKey, etc.)
- Leverage Flow for reactive updates
- Implement repository pattern for encapsulation
- Use stateIn for Compose StateFlow integration
- Migrate gradually from SharedPreferences
8 Android app templates: Gumroad
Top comments (0)