Blog Infos
Author
Published
Topics
, , ,
Author
Published

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.

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Building State Holders in Compose with Molecule: A New Approach to Reusable UI Components

Are your ViewModels exponentially growing out of control as they manage the state for each of your Composables? This talk introduces Molecule, a new library for creating state holders in Jetpack Compose.
Watch Video

Building State Holders in Compose with Molecule: A New Approach to Reusable UI Components

Jack Adams
Senion Android Engineer
Trainline

Building State Holders in Compose with Molecule: A New Approach to Reusable UI Components

Jack Adams
Senion Android Engin ...
Trainline

Building State Holders in Compose with Molecule: A New Approach to Reusable UI Components

Jack Adams
Senion Android Engineer
Trainline

Jobs

No results found.

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.

This article is previously published on proandroiddev.com.

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
In the world of Jetpack Compose, where designing reusable and customizable UI components is…
READ MORE
blog

How to animate BottomSheet content using Jetpack Compose

Early this year I started a new pet project for listening to random radio…
READ MORE
Menu