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
CodePudding user response:
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