Android Kotlin Working With Sealed Class To Update UI From Flow

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")
                        }
                    }
                }
            }
        }
    }
}


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *