androidkotlinandroid-jetpack-composeandroid-jetpack-compose-lazy-column

View API games grouped by league id only got first league with itemIndexed


I'm trying to get matches by date from a football API and show them grouped by league. So, I've made a mutable list in ViewModel to store every match item with the fixtureId as its unique identifier. The problem is that items are added to the list in every call from the API without overwriting the old ones, so the list keeps growing in every call, and only one league (which contains two games that day) is shown with an infinite repetition. When changing the date, the new data is added to the list along with the old ones. I want to access the results in the following image.

If anyone can help, thank you.

@Composable
fun TodayMatchesLazy(
    matchesList: List<TodayResponseItem?>
) {
    val viewModel: MainViewModel = hiltViewModel()
    matchesList.forEach {
        viewModel.mainList
            .add(MatchesByLeague(
                leagueId = it!!.league!!.id!!,
                leagueName = it.league!!.name!!,
                leagueLogo = it.league.logo!!,
                matchId = it.fixture!!.id!!,
                teamHomeName = it.teams!!.home!!.name!!,
                teamHomeLogo = it.teams.home!!.logo!!,
                teamAwayName = it.teams.away!!.name!!,
                teamAwayLogo = it.teams.away.logo!!,
                teamHomeR = it.goals!!.home.toString(),
                teamAwayR = it.goals.away.toString(),
                time = it.fixture.date!!)
            )
    }
    val groupedList = viewModel.mainList.groupBy { it.leagueId }
    if (groupedList .isNotEmpty()) {
        LazyColumn(
            modifier = Modifier
                .padding(20.dp)
                .fillMaxSize()
        ) {
            groupedList .forEach { (league, items) ->
                item {
                    Row {
                        TodayMatchesHeader(leagueItem = matchesList, league = league)
                    }
                }
                itemsIndexed(items,
                    itemContent = { index, item ->
                        TodayMatchesRow(
                            teamHomeName = item.teamHomeName,
                            teamHomeLogo = item.teamHomeLogo,
                            teamHomeR = item.teamHomeR,
                            teamAwayName = item.teamAwayName,
                            teamAwayLogo = item.teamAwayLogo,
                            teamAwayR = item.teamAwayR,
                            time = item.time
                        )
                    }
                )
            }
        }
    }
}

//ViewModel

@HiltViewModel
class MainViewModel @Inject constructor(private val liveMatchesRepository: MainRepository): ViewModel() {
    private var _mainList = mutableStateListOf<MatchesByLeague>()
    val mainList: MutableList<MatchesByLeague> = _mainList
}
data class MatchesByLeague(
    val leagueId: Int, val leagueName: String, val leagueLogo: String,
    val matchId: Int, val teamHomeName: String, val teamHomeLogo: String,
    val teamAwayName: String, val teamAwayLogo: String,
    val teamHomeR: String, val teamAwayR: String, val time: String
)

UPDATE:

Based on the answer by BenjyTec, I've made some updates to the code. Now, it's working perfectly.

@Composable
fun TodayMatchesLazy(
    matchesList: List<TodayResponseItem?>
) {
    val viewModel: MainViewModel = hiltViewModel()
    viewModel.mainList.value = matchesList
    val groupedList = viewModel.mainList.value.groupBy { it!!.league!!.id }
    if (groupedList.isNotEmpty()) {
        LazyColumn(
            modifier = Modifier
                .padding(20.dp)
                .fillMaxSize()
        ) {
            groupedList.forEach { (league, items) ->
                item {
                    TodayMatchesHeader(leagueItem = items[0]!!, league = league!!)
                }
                itemsIndexed(items,
                    itemContent = { index, item ->
                        TodayMatchesRow(
                            teamHomeName = item!!.teams!!.home!!.name!!,
                            teamHomeLogo = item.teams!!.home!!.logo!!,
                            teamHomeR = item!!.goals!!.home!!.toString(),
                            teamAwayName = item.teams.away!!.name!!,
                            teamAwayLogo = item.teams.away.logo!!,
                            teamAwayR = item.goals!!.away!!.toString(),
                            time = item.fixture!!.date!!
                        )
                    }
                )
            }
        }
    }
}

And the viewModel based on BenjyTec answer.

@HiltViewModel
class MainViewModel @Inject constructor(private val liveMatchesRepository: MainRepository): ViewModel() {
    private var _mainList = mutableStateOf<List<TodayResponseItem?>>(listOf())
    var mainList: MutableState<List<TodayResponseItem?>> = _mainList
}

Solution

  • You are probably adding the elements to the _mainList as follows:

    _mainList.add(/* some code */)
    

    If you want the new data to replace the old one, you need to clear the list before adding the new items:

    _mainList.clear()
    _mainList = /* call function that returns List from API */
    

    In that case, you might use a normal mutableStateOf() instead of mutableStateListOf(). The mutableStateListOf() especially is useful when you want to add or remove single items from the list, which seems not to be needed in your case.

    private var _mainList = mutableStateOf<List<MatchesByLeague>>(listOf())
    // assign new list
    _mainList = /* call function that returns List from API */