Scrolling

Scroll-enabled containers and content.

Report on GitHub

Any lv_obj_t whose content exceeds its bounds can scroll, with per-axis control and optional snap points that lock motion to child boundaries. Scrollbars are themeable through the LV_PART_SCROLLBAR style part, and event callbacks let you translate, fade, or scale children as they move through the viewport. The examples cover scrollbar mode and styling, RTL placement, the scrollable/chain/snap/scroll-one/floating behaviors, scroll events, programmatic scrolling, and the infinite and circular scroll patterns.

Scroll chaining

A nested scrollable can stop scrolling from propagating to its parent.

The outer panel scrolls, and it contains an inner scrollable box that also overflows. By default, reaching the inner box's edge would "chain" the remaining scroll to the outer panel. Setting scroll_chain="false" on the inner box breaks that link, so scrolling stops at the box and the outer panel does not move.

Endless circular scrolling

Wrap items from one edge to the other so a strip scrolls without limits.

A row strip and a column strip each hold ten buttons. Their LV_EVENT_SCROLL callback detects when either edge is reached and uses lv_obj_move_to_index to move the boundary child across, then compensates with lv_obj_scroll_to_x/y so the visible content does not jump — making the finite list feel infinite in both directions.

Reacting to scroll events

Read the live scroll position from an LV_EVENT_SCROLL callback.

A panel overflows with buttons so it can be scrolled. On every LV_EVENT_SCROLL the handler reads lv_obj_get_scroll_y, lv_obj_get_scroll_top and lv_obj_get_scroll_bottom and writes them into a label, so the numbers update continuously as the panel is dragged — the typical way to drive UI from the scroll position.

Floating button over a list

A floating child stays pinned while the list behind it scrolls.

The panel is a scrollable list of buttons. The round "+" button sets floating="true": a floating child is ignored by the layout and is not moved when its parent scrolls, so it stays anchored to the bottom-right corner while the list slides underneath it.

Infinite (virtualized) scrolling

Create rows on demand and delete far-off ones while a column scrolls.

A column tracks the highest and lowest loaded numbers. The LV_EVENT_SCROLL callback appends or prepends rows while either edge is within 200 px of content end, and deletes rows once they are more than 600 px away, compensating each change with lv_obj_scroll_by so the view stays steady. This keeps the element count bounded no matter how far the user scrolls.

Scroll one at a time

Restrict each scroll gesture to a single snappable child.

The panel combines centre snapping with scroll_one="true". Snapping alone lets a fast fling cross several panels before settling; with "scroll one" enabled every gesture advances by exactly one panel, no matter how hard it is thrown. It requires snappable children and a snap mode other than none.

Scroll basics

A panel becomes scrollable on its own when its children overflow it.

The panel is only 140 px tall but stacks six full-width buttons, so the content is taller than the panel. LVGL makes the panel scrollable and shows a vertical scrollbar automatically — no scroll-specific API is used here, only content that exceeds the bounds.

Toggling scroll behaviour flags

Switch SCROLLABLE, SCROLL_CHAIN, SCROLL_ELASTIC and SCROLL_MOMENTUM on live.

These four flags change how a Widget reacts to scrolling but only show their effect through interaction, so one example covers them together. A scrollable list sits above four switches; each switch toggles the corresponding behaviour on the list with its dedicated setter (e.g. lv_obj_set_scrollable), so the change can be felt immediately by dragging the list.

Right-to-left scrolling

An RTL base direction moves the vertical scrollbar to the left side.

style_base_dir="rtl" flips the panel's base direction. The vertical scrollbar is then drawn on the left edge instead of the right, and the property is inherited, so setting it once on the panel is enough. The content itself is ordinary so the placement change is the only visible difference.

Disabling scrolling

Clearing the SCROLLABLE flag clips overflowing content instead of scrolling it.

Both panels hold the same overflowing column of buttons. The left one keeps the default behaviour and scrolls. The right one sets scrollable="false", so the flag is cleared: the extra content is simply clipped and the panel cannot be scrolled at all.

Scrollbar mode

Compare an always-hidden scrollbar with an always-visible one.

Two identical overflowing panels differ only in scrollbar_mode: the left one is off (scrollable, but the bar is never drawn), the right one is on (the bar is always drawn even when idle). The other modes are auto (bar only while scrollable) and active (bar only while actively scrolling).

Styling the scrollbar

Restyle the SCROLLBAR part into a thick, rounded, coloured bar.

The scrollbar is the scrollbar part of any scrollable Widget. A named style attached with selector="scrollbar" overrides its width (thickness), radius, fill colour/opacity and pad_right (gap from the edge). scrollbar_mode="on" keeps it visible so the styling is always seen.

Scroll snapping

Snap children to the centre, and opt one child out of snapping.

The row panel sets scroll_snap_x="center", so when a drag is released the nearest panel animates to the centre. The third button sets snappable="false", removing it from the snap candidates: scrolling glides over it and lands on a neighbouring, snappable panel instead.

Scrolling programmatically

Jump, save and restore the scroll position from code with animation.

The panel scrolls because its content overflows. Four buttons drive it without any user gesture: "Top" and "Bottom" animate to the extremes via lv_obj_scroll_to_y with LV_ANIM_ON; "Save" records the current lv_obj_get_scroll_y; "Restore" animates back to the saved offset.

Translate children while scrolling

Bow a column of buttons along a circle as they scroll past the centre.

A circular, corner-clipped container snaps its children vertically. An LV_EVENT_SCROLL callback measures each child's offset from the container centre, projects it onto a circle with lv_sqrt, writes the result to translate_x, and fades distant items toward LV_OPA_TRANSP — a parallax effect XML styling cannot express.

Last updated on

On this page