VirtualList
Render large variable-size linear collections lazily while preserving a stable visible anchor.
Render a large variable-size linear collection lazily while preserving a stable visible item as data and item sizes change.
Contract
| Axis | Contract |
|---|---|
| Identity | itemKey must return a unique, stable key. Keys preserve item state and define the anchor across insertions, removals, reordering, and size changes. |
| Measurement | estimatedItemExtent seeds unmeasured items. Natural layout replaces each estimate, so total extent and the scrollbar thumb are approximate until measurement completes. |
| Following | follow stays pinned only while the reader is already at that logical edge. Scrolling away preserves the visible anchor; returning to the edge enables following again. Call holdVisibleAnchorForNextLayout() before a disclosure resize that must override following once. |
| Loading | Leading and trailing edge requests signal the consumer once per requestKey and can render a measured status slot. The consumer owns fetching, errors, retry attempts, cursors, and completion. |
| Restoration | Pass an opaque initialSnapshot or set pageStorageId. A compatible snapshot wins over initialPosition; missing keys fall back to the initial position. |
| Access | Only the visible and cached range exists in the widget tree. Offscreen local state, full-document search, print, selection, and assistive virtual-cursor access are consumer concerns; use non-virtual rendering when the whole document must remain available. |
The list owns one bounded viewport and keeps data in forward order. It preserves one surviving visible item wrapper at the same viewport-relative coordinate. If a mutation crosses the visible range, every visible item cannot remain fixed at once.
Install
Add the package, then import its public library.
flutter pub add tinyrack_uiimport 'package:tinyrack_ui/tinyrack_ui.dart';Playground
Usage
import 'package:material_ui/material_ui.dart';
import 'package:tinyrack_ui/tinyrack_ui.dart';
TRVirtualList<Message, String>(
items: messages,
itemKey: (message) => message.id,
estimatedItemExtent: (message, index) => TRMeasurements.measureSm,
initialPosition: const TRVirtualListInitialPosition.trailing(),
follow: TRVirtualListFollow.trailing,
itemBuilder: (context, message, index) => MessageRow(message),
)Examples
Load older rows without moving the reader permalink
Change requestKey with each cursor or retry attempt. The consumer owns fetching and the status slot while the list preserves the visible anchor.
API
TRVirtualList properties
| Prop | Type / default | Purpose |
|---|---|---|
items / itemKey / itemBuilder | List<T> / K Function(T) / Widget Function(...) · required | Provide the ordered data, stable identity, and lazy row builder. |
estimatedItemExtent | double Function(T, int) · required | Estimates an unmeasured item main-axis extent. |
axis / initialPosition / follow | Axis / TRVirtualListInitialPosition<K> / TRVirtualListFollow | Choose vertical or horizontal layout, the fallback starting point, and optional logical-edge following. |
leadingEdgeRequest / trailingEdgeRequest | TRVirtualListEdgeRequest? · null | Signal proximity to either edge and optionally render a status slot. |
initialSnapshot / pageStorageId | TRVirtualListSnapshot<K>? / String? | Restore a stable-key anchor explicitly or through PageStorage. |
controller | TRVirtualListController<K>? · null | Navigate by index, key, or edge; capture a snapshot; or hold one layout anchor. |