Bird
Raised Fist0
Android Kotlinmobile~20 mins

SharedFlow for events in Android Kotlin - Mini App: Build & Ship

Choose your learning style10 modes available
Build: EventEmitterScreen
This screen demonstrates how to use SharedFlow to emit and collect one-time events in an Android Kotlin app. It has a button that when clicked emits an event, and a text area that shows the latest event received.
Target UI
-------------------------
| Event Emitter Screen   |
-------------------------
|                       |
| [Emit Event]           |
|                       |
| Last Event:            |
|                       |
|                       |
-------------------------
Use MutableSharedFlow to emit string events when the button is clicked
Collect the SharedFlow in a lifecycle-aware way to update the UI with the latest event
Display the last emitted event below the button
Use Kotlin coroutines and Flow APIs
Ensure events are handled only once and UI updates accordingly
Starter Code
Android Kotlin
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
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.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch

class EventEmitterScreen : ComponentActivity() {
    private val _eventFlow = MutableSharedFlow<String>()
    private val eventFlow = _eventFlow.asSharedFlow()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                Surface(modifier = Modifier.padding(16.dp)) {
                    // TODO: Add UI and collect events here
                }
            }
        }
    }
}
Task 1
Task 2
Task 3
Solution
Android Kotlin
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
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.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch

class EventEmitterScreen : ComponentActivity() {
    private val _eventFlow = MutableSharedFlow<String>()
    private val eventFlow = _eventFlow.asSharedFlow()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                Surface(modifier = Modifier.padding(16.dp)) {
                    val scope = rememberCoroutineScope()
                    var lastEvent by remember { mutableStateOf("No events yet") }

                    LaunchedEffect(Unit) {
                        eventFlow.collect { event ->
                            lastEvent = event
                        }
                    }

                    Column {
                        Button(onClick = {
                            scope.launch {
                                _eventFlow.emit("Event at " + System.currentTimeMillis())
                            }
                        }) {
                            Text("Emit Event")
                        }
                        Text(text = "Last Event: $lastEvent", modifier = Modifier.padding(top = 16.dp))
                    }
                }
            }
        }
    }
}

This solution uses MutableSharedFlow to emit string events when the button is clicked. The eventFlow is collected inside a LaunchedEffect to update the lastEvent state variable whenever a new event is emitted. The UI shows a button labeled "Emit Event" and below it the last event received. The coroutine scope is used to emit events asynchronously. This setup ensures events are handled only once and the UI updates reactively.

Final Result
Completed Screen
-------------------------
| Event Emitter Screen   |
-------------------------
|                       |
| [Emit Event]           |
|                       |
| Last Event:            |
| Event at 1680000000000 |
|                       |
-------------------------
When the user taps the 'Emit Event' button, a new event string with the current timestamp is emitted.
The text below updates to show the latest event string.
Events are handled one at a time and the UI always shows the most recent event.
Stretch Goal
Add a Snackbar that briefly shows the event message each time an event is emitted.
💡 Hint
Use ScaffoldState and rememberCoroutineScope to show Snackbar inside the button click coroutine.

Practice

(1/5)
1. What is the primary purpose of using SharedFlow in Android Kotlin apps?
easy
A. To manage background threads automatically
B. To store large amounts of data persistently
C. To send events to multiple listeners safely and efficiently
D. To replace all UI components with reactive streams

Solution

  1. Step 1: Understand SharedFlow's role

    SharedFlow is designed to broadcast events to multiple collectors without losing any events.
  2. Step 2: Compare with other options

    It is not for data storage, UI replacement, or thread management.
  3. Final Answer:

    To send events to multiple listeners safely and efficiently -> Option C
  4. Quick Check:

    SharedFlow = event broadcasting [OK]
Hint: SharedFlow broadcasts events to many listeners [OK]
Common Mistakes:
  • Confusing SharedFlow with data storage solutions
  • Thinking SharedFlow manages threads automatically
  • Assuming SharedFlow replaces UI components
2. Which of the following is the correct way to emit an event using a MutableSharedFlow named eventFlow?
easy
A. eventFlow.emit("New Event")
B. eventFlow.collect { "New Event" }
C. eventFlow.send("New Event")
D. eventFlow.postValue("New Event")

Solution

  1. Step 1: Identify the correct emission method

    MutableSharedFlow uses emit() to send events.
  2. Step 2: Eliminate incorrect methods

    collect() is for receiving, send() is for Channels, and postValue() is for LiveData.
  3. Final Answer:

    eventFlow.emit("New Event") -> Option A
  4. Quick Check:

    Emit events with emit() [OK]
Hint: Use emit() to send events on SharedFlow [OK]
Common Mistakes:
  • Using collect() to send events
  • Confusing SharedFlow with Channel's send()
  • Using LiveData's postValue() with SharedFlow
3. Given the following code snippet, what will be printed when the event is emitted?
val eventFlow = MutableSharedFlow<String>()

fun observe() {
  eventFlow.collect { println(it) }
}

fun sendEvent() {
  eventFlow.emit("Hello")
}

sendEvent()
observe()
medium
A. Compilation error
B. Hello printed twice
C. No output
D. Hello

Solution

  1. Step 1: Analyze suspend function calls

    Both emit() and collect() are suspend functions called from non-suspend functions and top-level code without a coroutine scope.
  2. Step 2: Result of invalid calls

    Suspend functions cannot be called from non-suspend contexts, causing a compilation error.
  3. Final Answer:

    Compilation error -> Option A
  4. Quick Check:

    suspend emit/collect outside coroutine = compile error [OK]
Hint: emit() and collect() must be called in coroutines [OK]
Common Mistakes:
  • Overlooking that emit() and collect() are suspend functions
  • Assuming the code compiles and runs to test replay behavior
  • Forgetting coroutine requirements for Flow operations
4. Identify the problem in this code snippet using SharedFlow:
val eventFlow = MutableSharedFlow<String>()

fun start() {
  eventFlow.emit("Start")
}

fun listen() {
  eventFlow.collect { println(it) }
}

start()
listen()
medium
A. No problem, code works fine
B. collect() is called without a coroutine
C. MutableSharedFlow cannot emit strings
D. emit() is called outside a coroutine

Solution

  1. Step 1: Check emit() usage

    emit() is a suspend function and must be called inside a coroutine.
  2. Step 2: Check collect() usage

    collect() is also suspend and needs a coroutine, but the question focuses on emit() first.
  3. Final Answer:

    emit() is called outside a coroutine -> Option D
  4. Quick Check:

    emit() requires coroutine context [OK]
Hint: emit() must run inside a coroutine [OK]
Common Mistakes:
  • Calling emit() directly without coroutine
  • Ignoring suspend nature of collect()
  • Thinking MutableSharedFlow restricts data types
5. You want to show a toast message every time a button is clicked using SharedFlow. Which approach correctly ensures the toast shows for each click even if the UI is recreated?
hard
A. Use a MutableSharedFlow with no replay and emit on button click
B. Use a MutableSharedFlow with replay = 1 and emit on button click
C. Use a StateFlow to hold the last toast message and emit on button click
D. Use LiveData to send the toast message on button click

Solution

  1. Step 1: Understand event replay needs

    To show toast after UI recreation, events must replay at least once.
  2. Step 2: Choose correct flow type and replay

    MutableSharedFlow with replay=1 replays last event to new collectors, ensuring toast shows.
  3. Step 3: Compare other options

    No replay means missed events; StateFlow holds state, not events; LiveData is not SharedFlow.
  4. Final Answer:

    Use a MutableSharedFlow with replay = 1 and emit on button click -> Option B
  5. Quick Check:

    Replay 1 ensures event delivery after UI recreation [OK]
Hint: Set replay=1 to resend last event to new collectors [OK]
Common Mistakes:
  • Using no replay and missing events after UI changes
  • Confusing StateFlow state with event replay
  • Using LiveData instead of SharedFlow for events
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