model/User.kt
package com.example.myapplicationstate.model data class User { val id: Int, val name: String }
model/UiState.kt
package com.example.myapplicationstate.model sealed class UiState { object Loading: UiState() data class Success(val users: List<User>): UiState() data class Error(val message: String): UiState() }
種類 主な目的 特徴
sealed class 型の制限つき継承(状態分岐) 継承を制限し、when式で exhaustiveness(漏れなくチェック)できる
data class データ保持専用クラス 自動で toString, equals, copy などが生成される
package com.example.myapplicationstate.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch class UserViewModel : ViewModel(){ private val _uiState = MutableStateFlow<UiState>(UiState.Loading) val uiState: StateFlow<UiState> = _uiSate init { fetchUsers() } fun fetchUsers(){ viewModelScope.launch { _uiState.value = UiState.Loading delay(2000) val result = runCatching { getUsersFromApi() } _uiState.value =result.fold ( onSuccess={ UiState.Success(it) }, onFailure = { UiState.Error(it.message ?: "Unknown Error") } ) } } private suspend fun getUsersFromApi(): List<User> { // 成功/失敗を切り替えるための仮コード if ((0..1).random() == 0) { throw RuntimeException("通信エラー") } return listOf( User(1, "Alice"), User(2, "Bob"), User(3, "Charlie") ) } }
ui/UserScreen.kt
package com.example.myapplicationstate.ui.screen import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.material.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import model.UiState @Composable fun UserScreen(userViewModel: UserViewModel = viewModel()) { val uiState by userViewModel.uiState.collectAsState() when (uiState){ is UiState.Loading -> { Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } is UiState.Success -> { val users = (uiState as UiState.Success).users LazyColumn { items(users) { user -> Text( text = user.name, modifier = Modifier .fillMaxWidth() .padding(16.dp) ) } } } is UiState.Error -> { val message = (uiState as UiSatete.Error).message Column ( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { Text("エラー: $message", color = MaterialTheme.colors.error) Spacer(modifier = Modifier.height(16.dp)) Button(onClick = { userViewModel.fetchUsers() }) { Text("リトライ") } } } } }
## @Composable
@Composable fun Greeting(name: String) { Text(text = "Hello, $name!") }
@Composable fun MyScreen() { Column { Text("こんにちは") Button(onClick = { /* クリック処理 */ }) { Text("ボタン") } } }
MainActivityはアプリの最初の画面
アプリのエントリーポイント
androidではuiとロジックが分けられるMVVMが一般的