Search code examples
androidandroid-jetpack-composeretrofit2

Null pointer exception on API response retrofit2, despite the response operation being successful


Proplem:

I'm trying to pass the ID parameter to a dialog screen to view the required data by ID. Despite the response process being successfully done as shown in the stack trace, the venue's data always returns a NullPointerException. The same steps in the following lines with this API work perfectly with other parts of the project.

As you can see in this image, the response state is successful, and the API data is already fetched, but it still provides this error.

If anyone can help.

Error:

[![NullPointerException][1]][1]

API Response Steps:

//Dialog Screen

@Composable
fun FetchVenueInfo(
    onDismiss: ()-> Unit,
    onConfirm:()->Unit,
    mainViewModel: MainViewModel = hiltViewModel()
) {
    val context = LocalContext.current
    LaunchedEffect(key1 = Unit) {
        if (!mainViewModel.isVenuesByIdInitialized.value) {
            mainViewModel.getVenuesById(mainViewModel.venueId.value)
            mainViewModel.isVenuesByIdInitialized.value = true
        }
    }
    when (val state = mainViewModel.venuesByIdState.collectAsState().value) {
        is VenuesByIdState.Empty -> {  }
        is VenuesByIdState.Loading -> {  }
        is VenuesByIdState.Error -> {
            Toast.makeText(context, state.message, Toast.LENGTH_SHORT).show()
        }
        is VenuesByIdState.Success -> {
            CustomDialog(
                onDismiss = { onDismiss() },
                onConfirm = { onConfirm() },
                venuesResponse = state.data.body()!!
            )
        }
    }
}

@Composable
fun CustomDialog(
    onDismiss: ()-> Unit,
    onConfirm:()->Unit,
    venuesResponse: VenuesResponse
) {
    val dark = LocalTheme.current.isDark
    Dialog(
        onDismissRequest = {
            onDismiss()
        },
        properties = DialogProperties(
            usePlatformDefaultWidth = false
        )
    ) {
       Card(
            elevation = CardDefaults.cardElevation(8.dp),
            shape = RoundedCornerShape(15.dp),
            modifier = Modifier
                .fillMaxWidth(0.95f)
                .border(
                    2.dp, color = MaterialTheme.colorScheme.primary,
                    shape = RoundedCornerShape(15.dp)
                )
       ) {
            Column(
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(15.dp),
                verticalArrangement = Arrangement.spacedBy(25.dp)
            ){
                Text(venueResponse.name!!) // <- NullPointerException
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                ) {
                    AsyncImage(
                        model = venuesResponse.image, // <- NullPointerException
                        contentDescription = "Image"
                    )
                }
            }
       }
    }
}

//Opening Dialog onClick

@Composable
fun MainScreen(
    matchByIdResponseItem: MatchByIdResponseItem,
    mainViewModel: MainViewModel = hiltViewModel()
) {
    if (mainViewModel.isDialogShown) {
        FetchStadiumInfo(
            onDismiss = { mainViewModel.onDismiss() },
            onConfirm = {}
        )
    }
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .clickable(
                 interactionSource = remember { MutableInteractionSource() },
                 indication = ripple(
                      bounded = true,
                      color = MaterialTheme.colorScheme.primary
                 ),
                 onClick = {
                      mainViewModel.setId(matchByIdResponseItem.fixture!!.venue!!.id!!)
                      mainViewModel.onDialogOpened()
                 }
            )
            .padding(top = 1.dp),
       colors = CardDefaults.cardColors(
            containerColor = Color.Transparent
       )
    ) {
       //...
    }
}

Solution

  • Well, somehow not Venues class, but VenuesResponse is called Response<> by mistake within my code, and then the list will never be fetched. So, simply put, this is my data class: All I did was replace VenuesResponce with Venues in all steps:

    data class Venues(
    
        @field:SerializedName("response")
        val response: List<VenuesResponse?>? = null,
    
        @field:SerializedName("get")
        val get: String? = null,
    
        @field:SerializedName("paging")
        val paging: Paging? = null,
    
        @field:SerializedName("parameters")
        val parameters: Parameters? = null,
    
        @field:SerializedName("results")
        val results: Int? = null,
    
        @field:SerializedName("errors")
        val errors: List<Any?>? = null
    )
    
    //ex.
    interface MainApi {
        @Headers("api-key: $API_KEY")
        @GET(Constants.GET_VENUES)
        suspend fun getVenuesById(
            @Query(Constants.VENUE_BY_ID_PARAM) id: Int
        ): Response<Venues>
    }