Search code examples
androidkotlinretrofitandroid-jetpack-compose

GET request in retrofit in android is not working


I am trying to learn compose and retrofit and for that I am developing a very easy app, fetching jokes from a public API and showing them in a lazy list. But it is not working and I am not able to see any jokes. I am new to Kotlin and Jetpack compose. Please help me debug this.

I have a joke class

data class Joke(
    val id: Int,.
    val punchline: String,
    val setup: String,
    val type: String
)

This is the API I am GETing from: https://official-joke-api.appspot.com/jokes/:id This is the response:

{"type":"general","setup":"What did the fish say when it hit the wall?","punchline":"Dam.","id":1}

This is the retrofit api service:


const val BASE_URL = "https://official-joke-api.appspot.com/"

interface JokeRepository {

    @GET("jokes/{id}")
    suspend fun getJoke(@Path("id") id: String ) : Joke
    companion object {
        var apiService: JokeRepository? = null
        fun getInstance(): JokeRepository {
            if (apiService == null) {
                apiService = Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build().create(JokeRepository::class.java)
            }
            return apiService!!
        }
    }
}

This is the Jokes view model:

class JokeViewModel : ViewModel() {
    private val _jokeList = mutableListOf<Joke>()
    var errorMessage by mutableStateOf("")
    val jokeList: List<Joke> get() = _jokeList

    fun getJokeList() {
        viewModelScope.launch {
            val apiService = JokeRepository.getInstance()
            try {
                _jokeList.clear()
//                for(i in 1..100) {
//                    var jokeWithId = apiService.getJoke(i.toString())
//                    _jokeList.add(jokeWithId)
//                    Log.d("DEBUGGG", jokeWithId.setup)
//                }
                var joke = apiService.getJoke("1")
                _jokeList.add(joke)

            }
            catch (e: Exception) {
                errorMessage = e.message.toString()
            }
        }
    }
}

This is the Main Activity

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {

        val jokeViewModel = JokeViewModel()
        super.onCreate(savedInstanceState)
        setContent {
            HasyamTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    JokeView(jvm = jokeViewModel)
                }
            }
        }
    }
}

This is the Joke Component and view

@Composable
fun JokeView(jvm: JokeViewModel) {
    LaunchedEffect(Unit, block = {
        jvm.getJokeList()
    })
    
    Text(text = jvm.errorMessage)
    
    LazyColumn() {
        items(jvm.jokeList) {
            joke -> JokeComponent(joke)
        }
    }

}



@OptIn(ExperimentalMaterial3Api::class)

@Composable
fun JokeComponent(joke: Joke) {

    var opened by remember { mutableStateOf(false)}

    Column(
        modifier = Modifier.padding(15.dp)
    ) {
        Card(
            modifier = Modifier
                .fillMaxWidth()
                .clickable { },
            elevation = CardDefaults.cardElevation(
                defaultElevation = 5.dp
            ),

            onClick = { opened = !opened}

        ) {
            Text(modifier = Modifier.padding(15.dp), text = joke.setup)
        }

        if (opened) {
            Text(modifier = Modifier.padding(15.dp), text = joke.punchline)
        }
    }
}

Thank you so much


Solution

  • The issue here is that you are not using stateFlow. The screen is not recomposed so your LazyColumn is not recreated with the updated values.

    ViewModel

    class JokeViewModel : ViewModel() {
        var errorMessage by mutableStateOf("")
        private val _jokes = MutableStateFlow(emptyList<Joke>())
        val jokes = _jokes.asStateFlow()
    
        fun getJokeList() {
            viewModelScope.launch {
                val apiService = JokeRepository.getInstance()
                try {
                    var jokes = apiService.getJoke("1")
                    _jokes.update { jokes }
    
                } catch (e: Exception) {
                    errorMessage = e.message.toString()
                }
            }
        }
    }
    
    

    Joke View

    
    @Composable
    fun JokeView(jvm: JokeViewModel) {
        
        val jokes by jvm.jokes.collectAsState()
    
        LaunchedEffect(Unit, block = {
            jvm.getJokeList()
        })
    
        Text(text = jvm.errorMessage)
    
        LazyColumn {
            items(jokes) {
                    joke -> JokeComponent(joke)
            }
        }
    }
    
    
    

    You should read the following documentation about states : https://developer.android.com/jetpack/compose/state