ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

Managing Persistent State in Jetpack Compose

State management in Jetpack Compose becomes challenging when dealing with persistent data. Traditionally, developers rely on Android’s DataStore, but integrating it seamlessly with Compose can require excessive boilerplate. Let’s explore how to simplify this by building our own PreferenceState class to read and write data to dataStore just as you would read and update data from a state.

If you want to skip the implementation, you can check out the PreferenceState library that I developed. It provides the same functionality without the need to write boilerplate code. You can find it here: PreferenceState Library, or continue reading to understand how it works step by step.

Problem Statement

When you need to store data in DataStore and update the UI state based on it, the typical approach involves multiple layers: a ViewModel, a repository, and then collecting DataStore values to update the state. This results in excessive boilerplate code, making the implementation unnecessarily complex.

How We Solve This Problem

To simplify state persistence in Jetpack Compose, we will create a PreferenceState class that directly interacts with DataStore while behaving like a MutableState. This eliminates the need for a ViewModel or repository, reducing boilerplate code while keeping the logic concise and reusable.

Step-by-Step Implementation of PreferenceState

Step 1: Creating the PreferenceState Class

abstract class PreferenceState<T>(
private val key: Preferences.Key<T>,
private val defaultValue: T,
private val dataStore: DataStore<Preferences>
) : MutableState<T> {
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())

private val _state = mutableStateOf(defaultValue)

override var value: T
get() = _state.value
set(value) {
_state.value = value
scope.launch {
dataStore.edit { it[key] = value }
}
}

init {
scope.launch {
dataStore.data.map { it[key] ?: defaultValue }
.collect { newValue ->
withContext(Dispatchers.Main) {
_state.value = newValue
}
}
}
}

override fun component1(): T = value
override fun component2(): (T) -> Unit = { value = it }
}

Step 2: Creating a Custom Preference State Class

class UsernamePreferenceState: PreferenceState<String>(
key = stringPreferencesKey("username"),
defaultValue = "Guest",
dataStore = createDataStore()
)

Since creating a DataStore instance is straightforward, we are skipping that part.

Step 3: Creating a Remember Function for Compose

@Composable
fun rememberUsernamePreferenceState(): MarketViewSwitchState = remember {
UsernamePreferenceState()
}

Using PreferenceState in a Real-Life Scenario

Let’s say we want to persist a username input field across app restarts. We can use PreferenceState like this:

@Composable
fun UserProfileScreen(modifier: Modifier = Modifier) {
var username by rememberUsernamePreferenceState()

Column {
TextField(
value = username,
onValueChange = { username = it },
label = { Text("Username") }
)
}
}

This allows username to persist even when the app is restarted, without manually handling Flow collections or LaunchedEffect calls.

Conclusion

Building a PreferenceState class from scratch helps in understanding how DataStore integrates with Compose. However, for convenience, the PreferenceState library provides a ready-to-use implementation, making persistent state management easier in Jetpack Compose applications. You can find the library here: PreferenceState Library.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Written by Mohitsoni

Senior Mobile Developer at Punch. Android | iOS | KMM.

Responses (1)

Write a response