Einstellungen speichern mit DataStore – der moderne Ersatz für SharedPreferences
SharedPreferences ist alt, blockiert den Main-Thread und kennt keine Coroutinen. DataStore macht dasselbe – nur asynchron, typsicher und als Flow. So speicherst du Nutzer-Einstellungen richtig.
Wenn ich in einer Android-App eine Kleinigkeit merken will – Dark-Mode an/aus, die zuletzt gewählte Sprache, ob das Onboarding schon lief – greife ich nicht mehr zu SharedPreferences. Das ist Alt-API: Sie liest teils auf dem Main-Thread und passt nicht zu Coroutinen. Googles Nachfolger heißt DataStore – asynchron, mit Flow, ohne UI-Ruckler.
Einrichten
// build.gradle(.kts)
implementation("androidx.datastore:datastore-preferences:1.1.1")Einmal pro App einen DataStore an den Context hängen und die Schlüssel typsicher benennen:
import androidx.datastore.preferences.core.*
import androidx.datastore.preferences.preferencesDataStore
val Context.dataStore by preferencesDataStore(name = "settings")
object Keys {
val DARK_MODE = booleanPreferencesKey("dark_mode")
val USERNAME = stringPreferencesKey("username")
}Lesen – als Flow
DataStore gibt dir die Werte als Flow. Ändert sich etwas, bekommst du automatisch den neuen Wert – ideal fürs ViewModel.
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
fun darkModeFlow(context: Context): Flow<Boolean> =
context.dataStore.data.map { prefs -> prefs[Keys.DARK_MODE] ?: false }In Compose sammelst du das lebenszyklus-bewusst ein:
val darkMode by darkModeFlow(context).collectAsStateWithLifecycle(initialValue = false)Schreiben – suspend, nie blockierend
import androidx.datastore.preferences.core.edit
suspend fun setDarkMode(context: Context, enabled: Boolean) {
context.dataStore.edit { prefs -> prefs[Keys.DARK_MODE] = enabled }
}Der edit-Block ist eine suspend-Funktion und läuft transaktional – rufst du ihn aus einer Coroutine (z. B. viewModelScope.launch { ... }), blockiert nichts den Main-Thread.
Zwei Dinge, die ich mir gemerkt habe: DataStore ist für kleine Schlüssel-Werte-Paare (Einstellungen), nicht für Listen oder große Objekte – dafür nimmst du eine Datenbank wie Room. Und ?: default nicht vergessen, damit beim ersten Start ein sinnvoller Standardwert steht.
Wenn du eine Android-App planst, die sauber mit Einstellungen, State und Persistenz umgeht, baue ich dir das gern – meld dich über bymw.de.
Quellen
Du brauchst mehr als ein Snippet?
Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.
Projekt anfragen →Verwandte Snippets
Room-Migrationen – das Schema ändern, ohne Nutzerdaten zu verlieren
Eine neue Spalte in der Entity, und beim nächsten Start ist der Spielstand weg – weil fallbackToDestructiveMigration die Datenbank einfach löscht. So machst du es richtig, inklusive Test.
stateIn und shareIn – aus einem kalten Flow wird geteilter Zustand
Jeder Sammler eines kalten Flows startet die Arbeit neu – zwei Beobachter, zwei Datenbankabfragen. stateIn und shareIn machen daraus einen Strom, den sich alle teilen, samt aktuellem Wert und Abschalten bei Inaktivität.
viewModelScope – Coroutinen, die mit dem Bildschirm verschwinden
Eine Coroutine, die nach dem Schließen des Bildschirms weiterläuft, schreibt in ein ViewModel, das niemand mehr sieht – und hält im schlimmsten Fall die ganze Activity im Speicher. viewModelScope beendet sie automatisch.