Bird
Raised Fist0
Android Kotlinmobile~20 mins

StateFlow and SharedFlow in Android Kotlin - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Counter with StateFlow and SharedFlow
A simple Android screen that shows a counter value updated using StateFlow and emits one-time events using SharedFlow.
Target UI
-------------------------
| Counter App           |
|-----------------------|
| Count: 0              |
|                       |
| [Increment] [Reset]   |
|                       |
| Event: None           |
-------------------------
Display a count value that updates reactively using StateFlow.
Two buttons: Increment increases count by 1, Reset sets count to 0.
Use SharedFlow to emit one-time events when count reaches multiples of 5.
Show the latest event message below the buttons.
Use Kotlin coroutines and flows properly in ViewModel and Activity.
Starter Code
Android Kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    private val viewModel: CounterViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // TODO: Observe StateFlow and SharedFlow from viewModel
        // TODO: Set click listeners for buttons
    }
}

// TODO: Create CounterViewModel with StateFlow and SharedFlow
Task 1
Task 2
Task 3
Task 4
Solution
Android Kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import androidx.activity.viewModels
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    private val viewModel: CounterViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val countText = findViewById<TextView>(R.id.countText)
        val eventText = findViewById<TextView>(R.id.eventText)
        val incrementButton = findViewById<Button>(R.id.incrementButton)
        val resetButton = findViewById<Button>(R.id.resetButton)

        lifecycleScope.launch {
            viewModel.count.collect { value ->
                countText.text = "Count: $value"
            }
        }

        lifecycleScope.launch {
            viewModel.events.collect { event ->
                eventText.text = "Event: $event"
            }
        }

        incrementButton.setOnClickListener {
            viewModel.increment()
        }

        resetButton.setOnClickListener {
            viewModel.reset()
        }
    }
}

class CounterViewModel : ViewModel() {
    private val _count = MutableStateFlow(0)
    val count = _count.asStateFlow()

    private val _events = MutableSharedFlow<String>()
    val events = _events.asSharedFlow()

    fun increment() {
        val newValue = _count.value + 1
        _count.value = newValue
        if (newValue % 5 == 0) {
            viewModelScope.launch {
                _events.emit("Reached multiple of 5: $newValue")
            }
        }
    }

    fun reset() {
        _count.value = 0
        viewModelScope.launch {
            _events.emit("Counter reset to zero")
        }
    }
}

This app uses StateFlow to hold the current count value. StateFlow always has a current value and updates the UI reactively when the count changes.

We use SharedFlow to send one-time events like messages when the count reaches multiples of 5 or when reset. SharedFlow does not hold a value but emits events that subscribers can react to.

The CounterViewModel manages the state and events. The MainActivity collects these flows and updates the UI accordingly. Buttons call ViewModel functions to change state or emit events.

This separation keeps UI and logic clean and reactive.

Final Result
Completed Screen
-------------------------
| Counter App           |
|-----------------------|
| Count: 7              |
|                       |
| [Increment] [Reset]   |
|                       |
| Event: Reached multiple|
| of 5: 5               |
-------------------------
Tapping Increment increases the count by 1 and updates the Count text.
When count reaches 5, 10, 15, etc., an event message appears below buttons.
Tapping Reset sets count to 0 and shows 'Counter reset to zero' event.
Stretch Goal
Add a Toast message that shows the event text whenever a new event is emitted.
💡 Hint
Use lifecycleScope to collect events and call Toast.makeText(context, event, Toast.LENGTH_SHORT).show() inside the collector.

Practice

(1/5)
1. What is the main difference between StateFlow and SharedFlow in Kotlin?
easy
A. Both StateFlow and SharedFlow hold the latest value.
B. StateFlow holds the latest value, SharedFlow does not hold any value.
C. SharedFlow holds the latest value, StateFlow does not hold any value.
D. Neither StateFlow nor SharedFlow hold any value.

Solution

  1. Step 1: Understand StateFlow behavior

    StateFlow always holds the latest emitted value and replays it to new collectors.
  2. Step 2: Understand SharedFlow behavior

    SharedFlow emits events but does not hold or replay the latest value by default.
  3. Final Answer:

    StateFlow holds the latest value, SharedFlow does not hold any value. -> Option B
  4. Quick Check:

    StateFlow holds state, SharedFlow emits events [OK]
Hint: Remember: StateFlow = state holder, SharedFlow = event emitter [OK]
Common Mistakes:
  • Thinking SharedFlow holds the latest value like StateFlow
  • Confusing event emission with state holding
  • Assuming both behave the same way
2. Which of the following is the correct way to create a StateFlow with an initial value of 0?
easy
A. val stateFlow = StateFlow<Int>()
B. val stateFlow = MutableSharedFlow(0)
C. val stateFlow = MutableStateFlow(0)
D. val stateFlow = SharedFlow(0)

Solution

  1. Step 1: Identify correct constructor for StateFlow

    MutableStateFlow requires an initial value and is created with MutableStateFlow(initialValue).
  2. Step 2: Check other options

    MutableSharedFlow does not take an initial value; StateFlow is an interface and cannot be instantiated directly.
  3. Final Answer:

    val stateFlow = MutableStateFlow(0) -> Option C
  4. Quick Check:

    MutableStateFlow needs initial value [OK]
Hint: StateFlow needs initial value via MutableStateFlow constructor [OK]
Common Mistakes:
  • Trying to instantiate StateFlow interface directly
  • Using SharedFlow constructor for StateFlow
  • Passing initial value to MutableSharedFlow
3. Given the code below, what will be printed?
val stateFlow = MutableStateFlow(10)

stateFlow.value = 20

println(stateFlow.value)
medium
A. Compilation error
B. 10
C. null
D. 20

Solution

  1. Step 1: Understand MutableStateFlow value assignment

    Assigning stateFlow.value = 20 updates the current value held by the StateFlow.
  2. Step 2: Print the current value

    println(stateFlow.value) prints the latest value, which is 20.
  3. Final Answer:

    20 -> Option D
  4. Quick Check:

    StateFlow.value reflects latest assigned value [OK]
Hint: StateFlow.value always shows latest assigned value [OK]
Common Mistakes:
  • Thinking initial value 10 will print instead of updated 20
  • Confusing StateFlow with SharedFlow behavior
  • Expecting null or error due to reassignment
4. What is wrong with this code snippet using SharedFlow?
val sharedFlow = MutableSharedFlow<Int>()

sharedFlow.value = 5
medium
A. SharedFlow does not have a value property to assign.
B. You must initialize SharedFlow with an initial value.
C. You cannot create a MutableSharedFlow without parameters.
D. The code is correct and will compile.

Solution

  1. Step 1: Check SharedFlow properties

    SharedFlow does not hold state and has no value property to assign.
  2. Step 2: Understand how to emit values

    Values are emitted using emit() or tryEmit() functions, not by assigning value.
  3. Final Answer:

    SharedFlow does not have a value property to assign. -> Option A
  4. Quick Check:

    SharedFlow emits events, no value property [OK]
Hint: Use emit() to send values in SharedFlow, not value assignment [OK]
Common Mistakes:
  • Trying to assign value property on SharedFlow
  • Confusing SharedFlow with StateFlow
  • Assuming SharedFlow holds state like StateFlow
5. You want to create a SharedFlow that replays the last 3 emitted values to new collectors. Which configuration is correct?
hard
A. val sharedFlow = MutableSharedFlow<Int>(replay = 3)
B. val sharedFlow = MutableSharedFlow<Int>(replay = 0)
C. val sharedFlow = MutableStateFlow<Int>(3)
D. val sharedFlow = MutableSharedFlow<Int>(bufferSize = 3)

Solution

  1. Step 1: Understand replay parameter in SharedFlow

    The replay parameter defines how many past values are replayed to new collectors.
  2. Step 2: Check options for replaying last 3 values

    Setting replay = 3 in MutableSharedFlow correctly configures this behavior.
  3. Final Answer:

    val sharedFlow = MutableSharedFlow<Int>(replay = 3) -> Option A
  4. Quick Check:

    Replay count sets how many past events are replayed [OK]
Hint: Use replay parameter to replay past events in SharedFlow [OK]
Common Mistakes:
  • Confusing bufferSize with replay
  • Using StateFlow instead of SharedFlow for replay
  • Setting replay to 0 expecting replay behavior
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