Compose Theme: How to change light and dark mode dynamically with SharedFlow
In this article I want to propose a method to change from light mode to dark mode and vice versa using SharedFlow
.
Part I: Animated colors switching from light to dark mode
Flow and SharedFlow
First let’s look at what a Flow
is, let’s read the documentation:
In coroutines, a flow is a type that can emit multiple values sequentially, as opposed to suspend functions that return only a single value. For example, you can use a flow to receive live updates from a database.
Flows are built on top of coroutines and can provide multiple values. A flow is conceptually a stream of data that can be computed asynchronously. The emitted values must be of the same type. For example, a
Flow<Int>
is a flow that emits integer values.
What about SharedFlow
? It is a Flow API that enable flows to optimally emit state updates and emit values to multiple consumers.
As an example, you could use a
SharedFlow
to send ticks to the rest of the app so that all the content refreshes periodically at the same time.
Implementation
SharedFlow and Theme
First, we create our SharedFlow
which we will use to notify the selected mode change:
Where Theme
is an Enum
with the theme modes.
Use cases
Now, following Android architecture with Hilt, we define in the domain model two use cases to emit and read the SharedFlow
With GetThemeUpdateUseCase
we ask the Flow<Theme>
value and with PublishThemeUpdateUseCase
we emit the Theme
value.
ViewModels
In the ViewModels
we use the use cases above, in one of them we emit the Theme
value selected from a DropdownMenu
Selecting the light or the dark mode we call publishThemeUpdateUseCase
with the relative theme selected.
In the other ViewModel
we collect the Theme
value and update a State
variable to observe the change in the Compose screen.
Update Theme
Finally, in the MainActivity
we observe the theme
state value to update the MaterialTheme
, passing the value to our ComposeFuctionsTheme
In the following repository, you can find the completed runnable implementation, also we used DataStore
to save the value locally:
You can find a branch with the implementation of Material 3.
Thank you, I hope you enjoyed reading it.