LazyColumn — wat is het, efficiënte lijsten in Compose

Auteur: IT Sectr Gepubliceerd: 2026-02-24 Leestijd: 10 min

LazyColumn — Jetpack Compose-component voor het weergeven van scrollbare lijsten met lazy loading. In tegenstelling tot RecyclerView maakt LazyColumn geen ViewHolders aan en gebruikt geen XML — alle elementen worden declaratief beschreven via composable-functies. We laten zien wat LazyColumn is, hoe LazyListState en items API werken, en hoe je prestaties op 60 FPS-niveau kunt bereiken. Bij IT Sectr gebruiken we LazyColumn in al onze nieuwe Compose-projecten. Voor een vergelijking met de klassieke aanpak lees je het artikel over RecyclerView.

Belangrijkste punten

  • LazyColumn — Jetpack Compose-component voor verticale lijsten met lazy loading, onderdeel van Material Design 3.
  • LazyListState — lijststatus: scrollpositie, zichtbare elementen, programmatisch scrollen.
  • items API — functies items(), itemsIndexed(), items() met sleutels voor gegevensbinding.
  • key — parameter voor stabiele identificatie van elementen, waarmee Compose compositie kan hergebruiken.
  • LazyVerticalGrid — versie van LazyColumn voor rasters met een vast aantal kolommen.

Wat is LazyColumn in Jetpack Compose?

LazyColumn — is een composable-functie uit de Foundation Compose-bibliotheek die een verticale lijst met elementen weergeeft met lazy loading: alleen elementen die op het scherm zichtbaar zijn of zich in de buurt bevinden (prefetch-zone) worden aangemaakt en gecomponeerd. LazyColumn verscheen in Compose 1.0 (2021) samen met LazyRow (horizontale lijst) en LazyVerticalGrid (raster).

Lazy loading werkt via het SubcomposeLayout-mechanisme: LazyColumn meet de beschikbare ruimte, vraagt het zichtbare bereik op bij LayoutInfo en componeert alleen elementen in dat bereik. Elementen die van het scherm zijn verdwenen, verlaten de compositie (behalve de prefetch-buffer). Volgens Google-gegevens (Android Performance, 2026) levert LazyColumn 60 FPS bij het scrollen van een lijst met 10000+ elementen op middenklasse apparaten.

Verschil met Column + ScrollState

Column met verticaal scrollen — is de gewone plaatsing van alle elementen onder elkaar met toepassing van de verticalScroll-modifier. Column is niet lui: alle onderliggende elementen worden onmiddellijk gecomponeerd, zelfs als ze niet zichtbaar zijn. Voor een lijst met 200+ elementen veroorzaakt Column aanzienlijke vertraging bij het starten. LazyColumn componeert alleen zichtbare elementen + buffer (standaard 1 scherm vooruit/achteruit), wat het onmisbaar maakt voor lange lijsten.

items API: gegevens aan lijst koppelen

items API — set extensiefuncties voor LazyColumn die een gegevenslijst en een DSL-beschrijving voor elk element accepteren. Hoofdfuncties: items(count, key, itemContent) voor een vast aantal, items(list, key, itemContent) voor een lijst, itemsIndexed(list, key, itemContent) met index. De parameter key — verplichte voorwaarde voor prestaties — geeft Compose een stabiele identificatie van het element.

kotlin
// Basis LazyColumn met itemContent
@Composable
fun ArticleList(articles: List<Article>) {
    LazyColumn(
        modifier = Modifier.fillMaxSize(),
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        // Header — niet lui, altijd aan het begin
        item {
            Text("Laatste artikelen", style = MaterialTheme.typography.headlineMedium)
        }

        // Lijst van artikelen met sleutel op id
        items(articles, key = { it.id }) { article ->
            ArticleCard(
                title = article.title,
                summary = article.summary,
                onClick = { onArticleClick(article.id) }
            )
        }

        // Footer met laadindicator
        item {
            CircularProgressIndicator(
                modifier = Modifier.fillMaxWidth().padding(16.dp)
            )
        }
    }
}

// Aangepast lijstelement
@Composable
fun ArticleCard(title: String, summary: String, onClick: () -> Unit) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .clickable(onClick = onClick)
    ) {
        Column(modifier = Modifier.padding(12.dp)) {
            Text(text = title, style = MaterialTheme.typography.titleMedium)
            Spacer(modifier = Modifier.height(4.dp))
            Text(text = summary, style = MaterialTheme.typography.bodyMedium)
        }
    }
}

item {} vs items(): gebruik item {} voor afzonderlijke elementen die altijd in de lijst aanwezig zijn (header, footer, scheidingstekens). Gebruik items() voor een dynamische gegevenslijst. items() accepteert Iterable of Int en een DSL-blok dat voor elk element wordt aangeroepen. Combineer item en items in één LazyColumn: de volgorde van aanroepen bepaalt de weergavevolgorde.

LazyListState: scrollpositie beheren

LazyListState — object dat de lijststatus opslaat: huidige scrollpositie (firstVisibleItemIndex, firstVisibleItemScrollOffset), zichtbare elementen (layoutInfo) en methoden voor programmatisch scrollen (scrollToItem, animateScrollToItem). LazyListState wordt aangemaakt via rememberLazyListState() en doorgegeven aan LazyColumn via de parameter state.

kotlin
// LazyColumn met behoud van scrollpositie
@Composable
fun ScrollingList(items: List<Item>) {
    val listState = rememberLazyListState()

    Box(modifier = Modifier.fillMaxSize()) {
        LazyColumn(state = listState) {
            items(items, key = { it.id }) { item ->
                ListItemView(item = item)
            }
        }

        // Knop "Omhoog" verschijnt na het 10e element
        val showButton by remember {
            derivedStateOf { listState.firstVisibleItemIndex > 10 }
        }

        AnimatedVisibility(visible = showButton) {
            FloatingActionButton(
                modifier = Modifier
                    .align(Alignment.BottomEnd)
                    .padding(16.dp),
                onClick = {
                    coroutineScope.launch {
                        listState.animateScrollToItem(0)
                    }
                }
            ) {
                Icon(Icons.Default.KeyboardArrowUp, "Naar boven")
            }
        }
    }
}

// Informatie ophalen over zichtbare elementen
@Composable
fun ListDebugInfo(listState: LazyListState) {
    val visibleItems = listState.layoutInfo.visibleItemsInfo
    val totalItems = listState.layoutInfo.totalItemsCount
    val firstItem = visibleItems.firstOrNull()?.index
    val lastItem = visibleItems.lastOrNull()?.index

    Text("$firstItem–$lastItem van $totalItems getoond")
}

Programmatisch scrollen wordt uitgevoerd via de suspend-functies van listState: scrollToItem(index, scrollOffset) — onmiddellijke verplaatsing (zonder animatie), animateScrollToItem(index) — met animatie. Gebruik voor scrollen vanuit een coroutine LaunchedEffect of coroutineScope.launch. Belangrijk: roep geen suspend-functies aan vanuit compositie (binnen @Composable) — gebruik lambda's (onClick, LaunchedEffect).

LazyColumn vs RecyclerView: aanpakken vergeleken

LazyColumn en RecyclerView lossen dezelfde taak op — efficiënte weergave van lange lijsten. Het verschil zit in de architectuur: RecyclerView gebruikt ViewHolder en Adapter (klassieke Android View), LazyColumn — declaratieve composable-functies zonder XML en ViewHolder. De keuze hangt af van de technologiestack van het project: Compose vs View-based UI.

ParameterLazyColumn (Compose)RecyclerView (View)
LayoutComposable-functies (Kotlin DSL)XML-bestanden + ViewBinding
Adapteritems() / itemsIndexed() DSLRecyclerView.Adapter + ViewHolder
Sorteren/filterenSnapshot state + derivedStateOfDiffUtil + AsyncListDiffer
AnimatieAnimatedVisibility + Modifier.animateItemDefaultItemAnimator
Verschillende elementtypenitem {}/items() + when op typegetItemViewType + meerdere ViewHolders
Scrollen naar positielistState.animateScrollToItem()layoutManager.scrollToPosition()
PrefetchIn-process (SubcomposeLayout-buffer)RecycledViewPool + GapWorker
Netwerk/paginatiecollectAsLazyPagingItems() + Paging 3PagingDataAdapter + Paging 3

Google-advies (Android Developers, 2026): gebruik voor nieuwe projecten op Jetpack Compose LazyColumn. Gebruik voor View-based projecten RecyclerView. LazyColumn vereist Compose, wat ongeveer 3–5 MB aan de APK toevoegt — houd hier rekening mee bij ondersteuning van oude apparaten. Als het project al het View-systeem gebruikt, kan LazyColumn niet op hetzelfde scherm met XML-layout bestaan zonder AndroidView — in zulke gevallen is het eenvoudiger om bij RecyclerView te blijven.

LazyVerticalGrid en LazyHorizontalGrid: rasters

LazyVerticalGrid — versie van LazyColumn voor rasters met een vast aantal kolommen (columns). LazyHorizontalGrid — horizontaal raster met een vast aantal rijen (rows). Beide componenten gebruiken dezelfde principes van lazy loading en dezelfde items API als LazyColumn. De parameter columns bepaalt het aantal kolommen via GridCells.Fixed(N) of GridCells.Adaptive(minSize).

kotlin
// Raster met adaptieve kolommen
@Composable
fun PhotoGrid(photos: List<Photo>) {
    LazyVerticalGrid(
        columns = GridCells.Adaptive(minSize = 128.dp),
        contentPadding = PaddingValues(8.dp),
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        items(photos, key = { it.id }) { photo ->
            AsyncImage(
                model = photo.url,
                contentDescription = photo.title,
                modifier = Modifier.aspectRatio(1f)
            )
        }
    }
}

// Horizontale lijst (LazyRow)
@Composable
fun CategoryCarousel(categories: List<Category>) {
    LazyRow(
        contentPadding = PaddingValues(horizontal = 16.dp),
        horizontalArrangement = Arrangement.spacedBy(12.dp)
    ) {
        items(categories, key = { it.id }) { category ->
            FilterChip(
                selected = category.isSelected,
                onClick = { onCategoryClick(category.id) },
                label = { Text(category.name) }
            )
        }
    }
}

GridCells.Adaptive berekent automatisch het aantal kolommen op basis van de minimale celgrootte. Bijvoorbeeld, GridCells.Adaptive(128.dp) plaatst op een scherm van 360 dp breed 2 kolommen, op een tablet van 600 dp — 4 kolommen. GridCells.Fixed(2) toont altijd precies 2 kolommen. Adaptive heeft de voorkeur voor multi-scherm aanpassing — het past zich automatisch aan de schermbreedte aan zonder media-query's.

Prestaties van LazyColumn: key, contentType, prefetch

Prestaties van LazyColumn zijn afhankelijk van drie factoren: stabiliteit van sleutels (key), inhoudstype (contentType) en prefetch-buffer (beyondBounds). Standaard buffert LazyColumn 1 scherm vooruit en 1 achteruit. Google (Android Developers, 2026) adviseert deze parameters te configureren voor lijsten met verschillende elementtypen of complexe compositie.

ParameterBeschrijvingAanbeveling
keyStabiele identificatie van het elementGeef altijd key = { it.id } op. Zonder key gebruikt Compose de index en verstoort het herordenen van elementen de animatie.
contentTypeInhoudstype voor scheiding van poolsSpecificeer voor lijsten met verschillende elementtypen (tekst + afbeelding + advertentie). Compose hergebruikt compositie alleen binnen dezelfde contentType.
beyondBoundsAantal schermen voor prefetchVerhoog naar 2–3 voor lijsten met zware afbeeldingen. Standaard 1 scherm.
Modifier.animateItemAnimatie van positiewijzigingGebruik voor geanimeerd sorteren en filteren (Compose 1.7+).
kotlin
// Geoptimaliseerde LazyColumn met contentType en prefetch
@Composable
fun OptimizedFeed(items: List<FeedItem>) {
    val listState = rememberLazyListState()

    LazyColumn(
        state = listState,
        modifier = Modifier.fillMaxSize(),
        beyondBoundsPageCount = 2  // prefetch 2 schermen
    ) {
        items(
            items = items,
            key = { it.id },
            contentType = { it.type }  // scheiding op type
        ) { item ->
            when (item) {
                is FeedItem.Post -> PostView(item)
                is FeedItem.Ad -> AdView(item)
                is FeedItem.Suggested -> SuggestedView(item)
            }
        }
    }
}

// Prestatiemeting via listState
@Composable
fun ScrollPerformance(listState: LazyListState) {
    val scrollInfo = listState.layoutInfo
    val visibleCount = scrollInfo.visibleItemsInfo.size
    val total = scrollInfo.totalItemsCount

    // derivedStateOf — wordt niet opnieuw samengesteld als de waarde niet is veranderd
    val scrollProgress by remember {
        derivedStateOf {
            if (total > 0) visibleCount.toFloat() / total else 0f
        }
    }

    LinearProgressIndicator(
        progress = scrollProgress,
        modifier = Modifier.fillMaxWidth().height(2.dp)
    )
}

Veelvoorkomende fouten: (1) ontbreken van key — elementen worden opnieuw gecomponeerd bij elke wijziging van de lijst; (2) gebruik van mutableStateListOf zonder snapshot-streams — wijzigingen worden mogelijk niet bijgehouden; (3) aanroepen van suspend-functies binnen itemContent — onderbreekt compositie; (4) geen contentType voor gemengde typen — Compose hergebruikt de compositie van een advertentie voor een tekstbericht, wat visuele artefacten veroorzaakt. Bij IT Sectr voegen we contentType toe aan alle lijsten met drie of meer inhoudstypen.

Veelgestelde vragen

LazyColumn scrolt langzaam — wat te doen?

Controleer drie parameters: (1) key — zonder stabiele sleutel kan Compose compositie niet hergebruiken, (2) contentType — voor lijsten met verschillende elementtypen, (3) beyondBoundsPageCount — verhoog naar 2 voor afbeeldingen. Verplaats zware berekeningen binnen itemContent naar remember met een sleutel op basis van gegevens. Gebruik Modifier.drawWithContent in plaats van Image voor eenvoudige grafische weergave.

Hoe voeg ik een scheidingsteken toe tussen elementen van LazyColumn?

Gebruik LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) voor spaties tussen elementen. Voeg voor een visueel scheidingsteken met een lijn item { Divider() } toe tussen items. Automatisch scheidingsteken: items(items, key = { it.id }) { item -> ... }, en voeg tussen elk element item { HorizontalDivider() } in. Gebruik hiervoor LazyListScope.items(list, key) { /* element */ } en apart LazyListScope.item { Divider() }.

LazyColumn binnen Column werkt niet — waarom?

LazyColumn heeft standaard een oneindige hoogte (fillMaxSize). Binnen Column zonder vaste hoogte kan LazyColumn de grootte niet bepalen. Oplossing: (1) geef LazyColumn een vaste hoogte: Modifier.height(400.dp), (2) gebruik Modifier.weight(1f) binnen Column, (3) plaats LazyColumn niet in een verticaal scrollbare container — gebruik LazyColumn als root-element met item {} voor header en footer.

Hoe werk ik LazyColumn bij bij gegevenswijziging?

LazyColumn reageert automatisch op wijzigingen in State. Als gegevens worden opgeslagen in mutableStateListOf of mutableStateOf, componeert Compose alleen de gewijzigde elementen opnieuw. Gebruik voor lijsten uit ViewModel collectAsState() met Flow. Bij het bijwerken van de lijst met key animeert Compose automatisch wijzigingen (toevoegen, verwijderen, verplaatsen) via Modifier.animateItemPlacement().

LazyColumn vs LazyVerticalGrid — wat te kiezen?

LazyColumn — voor lijsten met één kolom (feed, chat, reacties). LazyVerticalGrid — voor rasters (galerij, catalogus, pictogrammen). Als elementen op de telefoon in één kolom en op de tablet in twee kolommen moeten staan — gebruik LazyVerticalGrid met GridCells.Adaptive. Als je een gemengd raster nodig hebt (verschillend aantal kolommen in één sectie) — gebruik LazyColumn en plaats binnen items horizontale LazyRow of geneste LazyVerticalGrid.

Samenvatting

  • LazyColumn — Jetpack Compose-component voor luie verticale lijsten met declaratieve API.
  • items() API accepteert een lijst en een DSL-blok voor elk element; key is verplicht voor prestaties.
  • LazyListState beheert de scrollpositie, biedt suspend-functies voor programmatisch scrollen.
  • contentType scheidt compositiepools voor lijsten met verschillende elementtypen.
  • LazyVerticalGrid / LazyRow — varianten voor rasters en horizontale lijsten met dezelfde items API.
  • LazyColumn levert 60 FPS voor 10000+ elementen dankzij SubcomposeLayout en prefetch-buffer.
  • Gebruik voor nieuwe Compose-projecten LazyColumn; voor View-based projecten — RecyclerView.

We ontwikkelen een mobiele applicatie turnkey

IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.

Bespreek het project

Lees ook