Bird
Raised Fist0
Android Kotlinmobile~20 mins

Side effects (LaunchedEffect, SideEffect) in Android Kotlin - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Side Effects Demo
This screen demonstrates how to use LaunchedEffect and SideEffect in Jetpack Compose to handle side effects like showing a message and logging.
Target UI
┌─────────────────────────────┐
│ Side Effects Demo            │
├─────────────────────────────┤
│ [Show Welcome Message]      │
│                             │
│ Log:                       │
│                             │
│                             │
└─────────────────────────────┘
Add a button labeled 'Show Welcome Message'.
When the button is clicked, use LaunchedEffect to show a Toast with 'Welcome to Side Effects Demo!'.
Use SideEffect to update a log text on the screen each time the button is clicked.
Display the log text below the button.
Ensure the UI updates correctly without blocking the main thread.
Starter Code
Android Kotlin
import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp

@Composable
fun SideEffectsDemo() {
    val context = LocalContext.current
    val clicked = remember { mutableStateOf(false) }
    val log = remember { mutableStateOf("") }

    Column(modifier = Modifier.padding(16.dp)) {
        Button(onClick = { /* TODO: Handle click */ }) {
            Text("Show Welcome Message")
        }

        Text(text = "Log: ${log.value}", modifier = Modifier.padding(top = 16.dp))

        // TODO: Add LaunchedEffect and SideEffect here
    }
}
Task 1
Task 2
Task 3
Solution
Android Kotlin
import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp

@Composable
fun SideEffectsDemo() {
    val context = LocalContext.current
    val clicked = remember { mutableStateOf(false) }
    val log = remember { mutableStateOf("") }

    Column(modifier = Modifier.padding(16.dp)) {
        Button(onClick = { clicked.value = true }) {
            Text("Show Welcome Message")
        }

        Text(text = "Log: ${log.value}", modifier = Modifier.padding(top = 16.dp))

        LaunchedEffect(clicked.value) {
            if (clicked.value) {
                Toast.makeText(context, "Welcome to Side Effects Demo!", Toast.LENGTH_SHORT).show()
            }
        }

        SideEffect {
            if (clicked.value) {
                log.value = "Button clicked at ${System.currentTimeMillis()}"
                clicked.value = false
            }
        }
    }
}

We use clicked state to track button clicks. When the button is clicked, clicked.value becomes true.

LaunchedEffect watches clicked.value. When it changes to true, it shows a Toast message asynchronously without blocking the UI.

SideEffect runs after every successful composition. It updates the log text with the current time and resets clicked.value to false to avoid repeated Toasts.

This way, the UI updates smoothly, showing the Toast and updating the log text below the button.

Final Result
Completed Screen
┌─────────────────────────────┐
│ Side Effects Demo            │
├─────────────────────────────┤
│ [Show Welcome Message]      │
│                             │
│ Log: Button clicked at 1680000000000 │
│                             │
└─────────────────────────────┘
When user taps 'Show Welcome Message', a Toast appears with 'Welcome to Side Effects Demo!'.
The log text updates below the button showing the timestamp of the click.
The button can be tapped multiple times to repeat the effect.
Stretch Goal
Add a counter that increments each time the button is clicked and display it next to the log.
💡 Hint
Use a separate mutableStateOf variable to track the count and update it inside SideEffect.

Practice

(1/5)
1. What is the main purpose of LaunchedEffect in Jetpack Compose?
easy
A. To run suspend functions when a key changes
B. To immediately update UI elements
C. To create a new composable function
D. To handle user input events

Solution

  1. Step 1: Understand LaunchedEffect role

    LaunchedEffect is designed to run suspend functions in response to changes in keys.
  2. Step 2: Differentiate from UI updates

    It does not directly update UI or handle input but manages side effects like network calls.
  3. Final Answer:

    To run suspend functions when a key changes -> Option A
  4. Quick Check:

    LaunchedEffect runs suspend code on key change = D [OK]
Hint: LaunchedEffect runs suspend code on key changes [OK]
Common Mistakes:
  • Confusing LaunchedEffect with UI rendering
  • Thinking it handles user input directly
  • Assuming it runs only once without key
2. Which of the following is the correct syntax to use SideEffect in a composable?
easy
A. SideEffect.launch { /* suspend code */ }
B. SideEffect { /* code to run after every recomposition */ }
C. SideEffect() { /* code to run once */ }
D. SideEffect.runOnUiThread { /* UI update code */ }

Solution

  1. Step 1: Recall SideEffect usage

    SideEffect is a composable function that takes a lambda to run after every successful recomposition.
  2. Step 2: Check syntax correctness

    SideEffect { /* code to run after every recomposition */ } matches the correct syntax without parentheses or suspend keywords.
  3. Final Answer:

    SideEffect { /* code to run after every recomposition */ } -> Option B
  4. Quick Check:

    SideEffect uses lambda syntax without parentheses = C [OK]
Hint: SideEffect uses simple lambda syntax without parentheses [OK]
Common Mistakes:
  • Adding parentheses after SideEffect
  • Trying to launch suspend code inside SideEffect
  • Using SideEffect as a coroutine builder
3. What will be the output behavior of this code snippet?
var count = 0
LaunchedEffect(Unit) {
  count += 1
}
SideEffect {
  println("Count: $count")
}
medium
A. Prints "Count: 0" repeatedly on every recomposition
B. Prints "Count: 1" repeatedly on every recomposition
C. Prints "Count: 1" once after first composition
D. Prints "Count: 0" once and never again

Solution

  1. Step 1: Analyze LaunchedEffect behavior

    LaunchedEffect(Unit) runs once on first composition and increments count to 1.
  2. Step 2: Analyze SideEffect behavior

    SideEffect runs after every recomposition and prints current count value. However, since count is a regular variable and not a state, recompositions do not trigger updates to count or cause recompositions to reflect the incremented value. The printed value remains 0 repeatedly.
  3. Final Answer:

    Prints "Count: 0" repeatedly on every recomposition -> Option A
  4. Quick Check:

    Regular variable not triggering recomposition; SideEffect prints initial value repeatedly = B [OK]
Hint: Regular variables don't trigger recomposition; SideEffect prints initial value repeatedly [OK]
Common Mistakes:
  • Assuming SideEffect runs only once
  • Thinking count increments on every recomposition
  • Confusing LaunchedEffect and SideEffect timing
4. Identify the error in this code using LaunchedEffect:
@Composable
fun MyScreen() {
  LaunchedEffect {
    delay(1000)
    println("Hello")
  }
}
medium
A. Cannot call delay inside LaunchedEffect
B. LaunchedEffect must be called outside composable
C. println cannot be used inside composable
D. Missing key parameter in LaunchedEffect call

Solution

  1. Step 1: Check LaunchedEffect syntax

    LaunchedEffect requires at least one key parameter to track changes and restart the coroutine.
  2. Step 2: Confirm other parts are valid

    Calling delay and println inside LaunchedEffect is allowed; it runs suspend code.
  3. Final Answer:

    Missing key parameter in LaunchedEffect call -> Option D
  4. Quick Check:

    LaunchedEffect needs key parameter = B [OK]
Hint: LaunchedEffect always needs a key parameter [OK]
Common Mistakes:
  • Omitting key parameter in LaunchedEffect
  • Thinking delay is disallowed inside LaunchedEffect
  • Believing println can't be used in composables
5. You want to fetch data from the network only once when a screen appears and log a message after every UI update. Which combination correctly uses LaunchedEffect and SideEffect?
hard
A. Use LaunchedEffect without key for fetching and SideEffect(Unit) for logging
B. Use SideEffect(Unit) for fetching and LaunchedEffect { println("UI updated") } for logging
C. Use LaunchedEffect(Unit) for fetching and SideEffect { println("UI updated") } for logging
D. Use SideEffect with key for fetching and LaunchedEffect(Unit) for logging

Solution

  1. Step 1: Identify correct usage for fetching once

    LaunchedEffect with Unit key runs once on first composition, ideal for fetching data.
  2. Step 2: Identify correct usage for logging after UI updates

    SideEffect runs after every recomposition, perfect for logging UI updates.
  3. Final Answer:

    Use LaunchedEffect(Unit) for fetching and SideEffect { println("UI updated") } for logging -> Option C
  4. Quick Check:

    LaunchedEffect for one-time suspend; SideEffect for every UI update = A [OK]
Hint: LaunchedEffect for one-time tasks; SideEffect for every UI update [OK]
Common Mistakes:
  • Using SideEffect for suspend fetching
  • Omitting key in LaunchedEffect
  • Confusing which runs once vs every recomposition
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard