Getting Started
Table of contents
- Installation
- Basic Usage with LoadState
- Basic Usage with MutationState
- Choosing the Right State Class
Installation
Add the Maven Central repository to your settings.gradle.kts (if not already present):
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}
Then add the dependency to your build.gradle.kts:
implementation("com.felipearpa:viewing-state:1.0.0")
Basic Usage with LoadState
LoadState is the simplest state class, managing four states: Idle, Loading, Loaded, and Failure.
ViewModel
data class MyData(val id: Int, val name: String)
class MyViewModel : ViewModel() {
private val _state = MutableStateFlow<LoadState<MyData>>(LoadState.Idle)
val state: StateFlow<LoadState<MyData>> = _state.asStateFlow()
fun fetchData() {
viewModelScope.launch {
_state.value = LoadState.Loading
try {
delay(2000)
val result = MyData(1, "Hello, Android!")
_state.value = LoadState.Loaded(result)
} catch (exception: Exception) {
_state.value = LoadState.Failure(exception)
}
}
}
fun resetState() {
_state.value = LoadState.Idle
}
}
Composable
@Composable
fun MyScreen(viewModel: MyViewModel = viewModel()) {
val viewModelState by viewModel.state.collectAsState()
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
)
when (val state = viewModelState) {
is LoadState.Idle -> {
Text("Click the button to load data.")
Button(onClick = { viewModel.fetchData() }) {
Text("Load Data")
}
}
is LoadState.Loading -> {
CircularProgressIndicator()
Text("Loading...")
}
is LoadState.Loaded -> {
Text("Data Loaded Successfully!")
Text("ID: ${state().id}, Name: ${state().name}")
Button(onClick = { viewModel.resetState() }) {
Text("Load Again")
}
}
is LoadState.Failure -> {
Text("Error: ${state().message}")
Button(onClick = { viewModel.fetchData() }) {
Text("Retry")
}
}
}
}
Basic Usage with MutationState
MutationState provides granular control over data mutations, tracking both original and updated values.
ViewModel
data class UserProfile(val displayName: String)
class ProfileViewModel : ViewModel() {
private val _state = MutableStateFlow<MutationState<UserProfile>>(
MutationState.Idle(UserProfile("Initial Name"))
)
val state = _state.asStateFlow()
fun updateDisplayName(newName: String) {
viewModelScope.launch {
val currentState = _state.value
val currentUserProfile = currentState.activeValue()
val targetProfile = UserProfile(newName)
_state.value =
MutationState.Mutating(original = currentUserProfile, updated = targetProfile)
try {
delay(1500)
if (newName.length < 3) {
throw IllegalArgumentException("Name must be at least 3 characters long.")
}
_state.value =
MutationState.Mutated(original = currentUserProfile, updated = targetProfile)
} catch (e: Exception) {
_state.value = MutationState.Failure(
original = currentUserProfile,
updated = targetProfile,
exception = e
)
}
}
}
fun resetToIdle(initialProfile: UserProfile? = null) {
val currentData = initialProfile ?: _state.value.activeValue()
_state.value = MutationState.Idle(currentData)
}
}
Composable
@Composable
fun ProfileEditScreen(viewModel: ProfileViewModel = viewModel()) {
val viewModelState by viewModel.state.collectAsState()
var editingName by remember { mutableStateOf("") }
LaunchedEffect(viewModelState) {
when (val state = viewModelState) {
is MutationState.Idle -> editingName = state.value.displayName
is MutationState.Mutated -> editingName = state.updated.displayName
else -> {}
}
}
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
OutlinedTextField(
value = editingName,
onValueChange = { editingName = it },
label = { Text("New Display Name") },
isError = viewModelState.isFailure()
)
Button(
onClick = { viewModel.updateDisplayName(editingName) },
enabled = !viewModelState.isMutating() && editingName.isNotBlank()
) {
Text("Save Name")
}
viewModelState.onFailure { _, _, exception ->
Text("Error: ${exception.message}", color = MaterialTheme.colorScheme.error)
}
}
}
Choosing the Right State Class
| Scenario | Recommended State |
|---|---|
| Fetching data from an API | LoadState |
| Submitting a form | SaveState |
| Editing existing data with undo capability | MutationState |
| Screen that loads data and allows edits | ResourceState |