Repository: android/compose-samples
File: JetNews/app/src/main/java/com/example/jetnews/ui/interests/InterestsScreen.kt (Kotlin)

Code before:
                    style = MaterialTheme.typography.subtitle2,
                    modifier = Modifier.paddingFromBaseline(top = 20.dp)
                )
            }
        }
    }
}

/**
 * Return state for interest sections grouped given the number of columns that fill the screen.
 * Sections are distributed to balance the number of items on each column.
 */
@Composable
private fun sectionsGroupedInColumnsAsState(
    sections: List<InterestSection>,
    columns: Int,
    defaultDispatcher: CoroutineDispatcher = Dispatchers.Default
): State<Array<MutableList<InterestSection>>> = produceState(
    initialValue = arrayOf(),
    keys = arrayOf(sections, columns)
) {
    withContext(defaultDispatcher) { // Move the execution of this logic to the background thread
        val sectionsInColumns = Array<MutableList<InterestSection>>(columns) { mutableListOf() }
        // For each section, find the column with less elements and add it there
        sections.forEach { section ->
            val shortestColumnIndex = sectionsInColumns.getIndexShortestElement()
            sectionsInColumns[shortestColumnIndex] += section
        }
        value = sectionsInColumns
    }
}

/**
 * Find the position of the array with less interests items in it
 */
private fun Array<MutableList<InterestSection>>.getIndexShortestElement(): Int {
    var (indexShortestColumn, elementsInShortestColumn) = Int.MAX_VALUE to Int.MAX_VALUE
    // For each column group, calculate the number of interests items and find the shortest
    this.forEachIndexed { index, columnsGroup ->
        val elements = columnsGroup.fold(0, { acc, section -> acc + section.interests.size })
        if (elements < elementsInShortestColumn) {
            indexShortestColumn = index
            elementsInShortestColumn = elements
        }
    }
    return indexShortestColumn
}

/**
 * Returns the max width for a Topic Item given maxWidth and number of columns constraints.
 */

Review comment (performance):
I think we can avoid having to search through all of the existing `sectionsInColumns` if we keep a running tally of the number of elements in each column. I think that should make it a bit more efficient as well (on the order of `O(sections.size * columns)` instead of `O(sections.size^2 * columns)`)

If we do that, we probably don't need to put this in the background thread either? It shouldn't be substantially slower that the older grouping method.

Code after:
            // Calculate the max width each item can take given the number of columns
            val itemMaxWidth = windowMaxWidth / columns
            // Limit the width of the item for them to not be too big given the available space
            if (windowMaxWidth < 800.dp) min(itemMaxWidth, 350.dp) else min(itemMaxWidth, 450.dp)
        }
    }

@Composable
fun rememberTabContent(
    interestsViewModel: InterestsViewModel
): List<TabContent> {
    // UiState of the InterestsScreen
    val uiState by interestsViewModel.uiState.collectAsState()

    // Describe the screen sections here since each section needs 2 states and 1 event.
    // Pass them to the stateless InterestsScreen using a tabContent.
    val topicsSection = TabContent(Sections.Topics) {
        val selectedTopics by interestsViewModel.selectedTopics.collectAsState()
        TopicList(
            topics = uiState.topics,
            selectedTopics = selectedTopics,
            onTopicSelect = { interestsViewModel.toggleTopicSelection(it) }
        )
    }

    val peopleSection = TabContent(Sections.People) {
        val selectedPeople by interestsViewModel.selectedPeople.collectAsState()
        PeopleList(
            people = uiState.people,
            selectedPeople = selectedPeople,
            onPersonSelect = { interestsViewModel.togglePersonSelected(it) }
        )
    }

    val publicationSection = TabContent(Sections.Publications) {
        val selectedPublications by interestsViewModel.selectedPublications.collectAsState()
        PublicationList(
            publications = uiState.publications,
            selectedPublications = selectedPublications,
            onPublicationSelect = { interestsViewModel.togglePublicationSelected(it) }
        )
    }

    return listOf(topicsSection, peopleSection, publicationSection)
}

@Preview("Interests screen", "Interests")
@Preview("Interests screen (dark)", "Interests", uiMode = UI_MODE_NIGHT_YES)
@Preview("Interests screen (big font)", "Interests", fontScale = 1.5f)
@Composable
fun PreviewInterestsScreenDrawer() {