Sitemap
Manish Kumar

Manish Kumar

Software Engineer with a decade of experience in building software applications using Android, Java, Kotlin, Spring Boot, Flutter, MySQL and ReactJS.

Jetpack Compose: Side Effects

4 min readDec 27, 2024

--

In Jetpack Compose, side effects refer to operations that occur outside the composable functions but are still tied to their lifecycle. These operations can include things like launching coroutines, performing UI updates, or cleaning up resources. Handling side effects correctly is critical to building performant and stable apps.

Get Manish Kumar’s stories in your inbox

Join Medium for free to get updates from this writer.

In this blog, we will cover the five main side-effect handlers in Jetpack Compose:

  1. LaunchedEffect: Launches coroutines for operations that should run on composable entry or when a key changes.
  2. rememberCoroutineScope: Provides a coroutine scope for UI interactions that need to run in a coroutine.
  3. SideEffect: Triggers simple side effects during recomposition (e.g., logging or UI updates).
  4. DisposableEffect: Handles resource cleanup when composables leave the composition.
  5. snapshotFlow: Allows you to observe state changes with a flow and react to them in a structured manner.

Each side effect handler has a unique use case and can be employed in different scenarios. Let’s explore them with code examples to understand their usage.

1. LaunchedEffect

LaunchedEffect is used to launch a coroutine and trigger a side effect when the composable enters the composition or when specific keys change. It’s great for asynchronous operations that you want to execute when a composable is created or when certain state changes.

Example: Using LaunchedEffect for Initial Setup

@Composable
fun LaunchedEffectDemo() {
// LaunchedEffect runs only once when the composable is first displayed
LaunchedEffect(Unit) {
// This block runs as a side effect
println("LaunchedEffect triggered!")
delay(1000L)
println("Side effect finished!")
}

Text("LaunchedEffect Demo")
}

Explanation:

  • LaunchedEffect(Unit) will trigger the coroutine when the composable is first created. If you want to trigger the LaunchEffect in recomposition then you need to pass the state variable as params in place of Unit.
  • The code inside LaunchedEffect will run asynchronously, allowing you to handle time-consuming operations like fetching data or updating state.

2. rememberCoroutineScope

rememberCoroutineScope provides a coroutine scope that can be used within a composable. This is particularly useful when you need to launch a coroutine in response to UI interactions or other state changes.

If you have priror experience of working with MVVM and coroutine then its similar to viewModelScope or lifeCycleScope

Example: Using rememberCoroutineScope for Button Click

@Composable
fun CoroutineScopeDemo() {
val scope = rememberCoroutineScope()

Button(onClick = {
scope.launch {
println("Button clicked, coroutine started")
delay(2000L)
println("Coroutine finished!")
}
}) {
Text("Start Coroutine")
}
}

Explanation:

  • rememberCoroutineScope() gives you a scope to launch a coroutine that runs within the composable’s lifecycle.
  • The coroutine starts when the button is clicked, simulating a long-running task.

3. SideEffect

SideEffect is used to trigger simple side effects like updating the system UI or performing any non-compositional tasks that should happen when the composable recomposes.

Example: Using SideEffect to Log Recomposition

@Composable
fun SideEffectDemo() {
var count = 0

// SideEffect is triggered every time the composable recomposes
SideEffect {
println("Recomposition occurred. Current count: $count")
}
Button(onClick = { count++ }) {
Text("Increment Count")
}
}

Explanation:

  • SideEffect is called every time the composable recomposes. This is ideal for logging or performing actions that don't directly modify the UI but are needed when recomposition occurs.

SideEffect will be triggered at the end of recomposition despite of location in code. Suppose you clicked the button and recompositon started, one TextField got recomposed and reached till end of method, then at last code inside SideEffect will be triggered

4. DisposableEffect

DisposableEffect is used to manage resources that need to be cleaned up when a composable leaves the composition. This can include operations like removing listeners, canceling coroutines, or releasing any external resources.

@Composable
fun DisposableEffectDemo() {
var count by remember { mutableStateOf(0) }

// Registering a listener or resource with DisposableEffect
DisposableEffect(Unit) {
println("Listener registered")
onDispose {
println("Listener unregistered")
}
}
Button(onClick = { count++ }) {
Text("Increase Count")
}
Text("Current count: $count")
}

Explanation:

  • DisposableEffect(Unit) allows you to perform operations when a composable enters the composition.
  • The onDispose block runs when the composable leaves the composition, which is useful for cleaning up resources such as listeners or network requests.

Basically its just the LaunchedEffect which provides separate block as onDispose to clear the resources.

5. snapshotFlow

snapshotFlow is used to create a Flow that observes changes to Compose state. It's great for reacting to state changes in a more structured, flow-based way, and is useful for when you want to track state over time.

Example: Using snapshotFlow to Collect State Changes

@Composable
fun SnapshotFlowDemo() {
var count by remember { mutableStateOf(0) }

// snapshotFlow collects state changes and triggers actions on change
LaunchedEffect(count) {
snapshotFlow { count } // Converts state to flow
.collect { newCount ->
println("Count has changed to: $newCount")
}
}
Button(onClick = { count++ }) {
Text("Increase Count")
}
Text("Current count: $count")
}

Explanation:

  • snapshotFlow { count } creates a Flow that emits the current value of count whenever it changes.
  • The collect function is used to observe state changes, and the side effect logic (in this case, logging) happens whenever the state is updated.

Happy coding!!

--

--

Written by Manish Kumar

Software Engineer with a decade of experience in building software applications using Android, Java, Kotlin, Spring Boot, Flutter, MySQL and ReactJS.