In this post I will show you an example of how I created a sample Sealed Class in Kotlin to update the UI from emitting and collecting from a Flow.
I am just jumping into it. The previous post here:
Android Working with a Flow to Update UI in Compose
started me into finding a cleaner and better way to capture the state of an API request. Then handle the UI updates depending on the state received.
As the last post, I am just going to post the code in as I have in my sample, together with least amount of class files. For better organization, it would be better to separate the classes.
I wanted to practice more with sealed classes to implement this so I could use them in a when statement on how to update the UI.
Code
This is what is inside the ViewModel class
class ViewModel(val applicationContext : Context) : ViewModel() {
private val resultRepo = ResultRepo(applicationContext)
var resultFlow = MutableStateFlow<ResultFromAPI>(ResultFromAPI.isLoading("Loading...."))
fun getToken(){
viewModelScope.launch {
resultRepo.getJWT().collect{
resultFlow.value = it
}
}
}
}
sealed class ResultFromAPI {
data class success(val data : String) : ResultFromAPI()
data class error(val exception : String) : ResultFromAPI()
data class isLoading(val message : String) : ResultFromAPI()
}
data class Item(
val item: String?
)
class ResultRepo(val context : Context){
suspend fun getJWT() : Flow<ResultFromAPI> {
return flow {
emit(ResultFromAPI.isLoading("Loading response..."))
delay(1000)
val service = RetrofitHelper.getInstance(context).create(AccessApi::class.java)
val response : Response<Item> = service.altertestJWT("Vegetables are healthy!")
if(response.isSuccessful){
val result = response.body()
if(result == null){
emit(ResultFromAPI.error("Empty response..."))
} else {
emit(ResultFromAPI.success(result.item.toString()))
}
} else {
emit(ResultFromAPI.error("Empty response..."))
}
}.flowOn(Dispatchers.IO)
}
}
class ViewModelFactory(val applicationContext : Context) : ViewModelProvider.Factory {
//------------to create a ViewModel. This will ensure same ViewModel is used
//val factory = ViewModelFactory()
//val viewModel = ViewModelProvider(this, factory)[ViewModel::class.java]//by viewModels<ViewModel>()
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return if(modelClass.isAssignableFrom(com.itgeek25.samplejwt.viewmodel.ViewModel::class.java)){
com.itgeek25.samplejwt.viewmodel.ViewModel(applicationContext) as T
} else {
throw IllegalArgumentException("ViewModel Not Found!!")
}
}
}
Now this is what is inside the MainActivity
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val factory = ViewModelFactory(applicationContext)
val viewModel = ViewModelProvider(this, factory)[ViewModel::class.java]
enableEdgeToEdge()
setContent {
SampleJWTTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
var change by remember { mutableStateOf(0) }
var resultFromAPI by remember { mutableStateOf<ResultFromAPI>(ResultFromAPI.isLoading("")) }
LaunchedEffect(Unit) {
viewModel.resultFlow.collect{
resultFromAPI = it
}
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.background(Color.Gray),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
when(resultFromAPI){
is ResultFromAPI.error -> {
Text(text = (resultFromAPI as ResultFromAPI.error).exception)
}
is ResultFromAPI.isLoading -> {
Text(text = (resultFromAPI as ResultFromAPI.isLoading).message)
}
is ResultFromAPI.success -> {
Text(text = (resultFromAPI as ResultFromAPI.success).data)
}
}
Button(onClick = {
viewModel.getToken()
}) {
Text(text = "Update")
}
}
}
}
}
}
}
Leave a Reply