0
0
Android Kotlinmobile~20 mins

StateFlow for reactive state in Android Kotlin - Mini App: Build & Ship

Choose your learning style9 modes available
Build: Counter Screen
A simple screen that shows a number and updates it reactively using StateFlow when the user taps a button.
Target UI
-------------------
|   Counter: 0    |
|                 |
|   [Increment]   |
-------------------
Use StateFlow in a ViewModel to hold the counter state.
Display the current counter value on the screen.
Add a button labeled 'Increment' that increases the counter by 1 when tapped.
The UI should update automatically when the counter changes.
Starter Code
Android Kotlin
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

class CounterViewModel : ViewModel() {
    // TODO: Create a private MutableStateFlow for the counter starting at 0
    // TODO: Expose a public StateFlow for the counter

    // TODO: Create a function to increment the counter
}

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: android.os.Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CounterScreen()
        }
    }
}

@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
    // TODO: Collect the counter StateFlow as state

    Scaffold(
        topBar = { TopAppBar(title = { Text("Counter Screen") }) },
        content = { paddingValues ->
            // TODO: Display the counter value and an Increment button
        }
    )
}
Task 1
Task 2
Task 3
Task 4
Task 5
Task 6
Solution
Android Kotlin
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class CounterViewModel : ViewModel() {
    private val _counter = MutableStateFlow(0)
    val counter: StateFlow<Int> = _counter.asStateFlow()

    fun increment() {
        _counter.value += 1
    }
}

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: android.os.Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CounterScreen()
        }
    }
}

@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
    val count by viewModel.counter.collectAsState()

    Scaffold(
        topBar = { TopAppBar(title = { Text("Counter Screen") }) },
        content = { paddingValues ->
            Column(
                modifier = androidx.compose.ui.Modifier
                    .fillMaxSize()
                    .padding(paddingValues)
                    .padding(16.dp),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = androidx.compose.ui.Alignment.CenterHorizontally
            ) {
                Text(text = "Counter: $count", style = MaterialTheme.typography.headlineMedium)
                Spacer(modifier = androidx.compose.ui.Modifier.height(24.dp))
                Button(onClick = { viewModel.increment() }) {
                    Text("Increment")
                }
            }
        }
    )
}

We use MutableStateFlow inside the CounterViewModel to hold the current count. This is private to prevent external modification. We expose a read-only StateFlow so the UI can observe changes.

The increment() function updates the counter value by increasing it by 1.

In the CounterScreen composable, we collect the StateFlow as Compose state using collectAsState(). This makes the UI automatically update when the counter changes.

The UI shows the current count in a Text and has a button labeled "Increment" that calls increment() when clicked.

Final Result
Completed Screen
-------------------
|   Counter: 5    |
|                 |
|   [Increment]   |
-------------------
When the user taps the 'Increment' button, the counter number increases by 1 immediately.
The displayed number updates reactively without needing to refresh the screen.
Stretch Goal
Add a 'Reset' button that sets the counter back to zero.
💡 Hint
Add a reset() function in the ViewModel that sets _counter.value = 0, then add a second Button in the UI that calls reset() when clicked.