Handling user selectable themes in Jetpack Compose

Recent versions of Android offer both Light and Dark themes, which the user can set globally via the device settings. Most apps nowadays follow this setting and change their theme based on the global setting, but it is also good practice to offer users an app option to set the theme regardless of what the global setting is. In this article we’ll find out how to do that with Jetpack Compose.

Storing the user preference

The first thing we need to do is persist the user setting; we will use SharedPreferences for this, though you may want to consider using the new DataStore — that’s just an implementation detail so we’ll use preferences here.

Creating the radio buttons

The next step is to create the composable that will show the 3 options to the user; for this we will use the selectableGroup modifier. Each entry will be its own composable, and we will also define a data class to represent the content of each entry. Let’s look at our radio group entry composables first

Radio group preview
Radio group preview

Putting it all together

Now that we have all the pieces we need it’s time to put it all together. For this simple example we will have the Activity listen for the app theme stream directly, but in a more realistic scenario this would be handled by a ViewModel and exposed as a state that the Activity ‘s composable would observe. Let’s have a look at the code first and we’ll go over it in detail in a moment

  • when we set the composable content, we observe the user setting for the app theme stream as a state, using the collectAsState extension function. This ensures that whenever the value changes any composables observing this value will be recomposed.
  • when we set our app theme, we specify if we are in light or dark theme based on the current value we received from the stream. If the value is Auto we default to the global setting, otherwise we enforce the value, either Light or Dark theme.
  • we display our main screen composable, providing the current value and a lambda to trigger when the user changes the app theme.
  • in the lambda we update the app theme in our persistent storage, which will trigger an update on the state flow and in turn trigger a recomposition of our screen.
  • we first build our list of radio group items, 3 entries with an ID and a label each. We use the enum’s ordinal as our ids.
  • our composable is a column where we display a label and our RadioGroup, using the items we just defined.
  • we provide the selected item id to the RadioGroup and in the click lambda we convert the id back to an enum and then pass this information back to our caller.
  • we add a button and some lorem ipsum text to aid in visualizing the theme changes.
App theme chooser screenshot
App theme chooser screenshot

Senior Android Developer