Lists, tables, and large data
Render collections with stable identity and virtualize data that should not all be laid out at once.
Key rows by domain identity
A row key should come from the record ID, not its current array index. Stable keys preserve focus, selection, animation, and retained node identity when sorting or filtering.
Element::column(rows.iter().map(|record| {
render_row(record).keyed(format!("record-{}", record.id))
}))Virtualize long collections
VList receives a stable key, row height, viewport height, and scroll offset. Its builder asks only for the visible range, so a 10,000-row model does not create 10,000 retained elements.
Keep selection in the model
Store sort order, selected record IDs, filters, and scroll state in the owning model. Derive visible rows during rendering or in a measured cache; do not duplicate the authoritative records inside view elements.
Reference files
These are the implementation and guide files used for this chapter.
docs/widgets/lists-tables.mdcrates/argui-widget-gallery/src/pages/data_table.rscrates/argui-widget-gallery/src/pages/data.rsA sortable, selectable data table
The Rust file below is imported verbatim by this page and compiled into the WebAssembly application running underneath it.
use argui::{
runtime::{Context, Render},
text::TextStyle,
ui::{Element, EventType, Sides, UiEventKind, VirtualList, length, percent},
widgets::default_theme,
};
pub struct Example {
list: VirtualList,
offset: f32,
}
impl Default for Example {
fn default() -> Self {
Self {
list: VirtualList::fixed(10_000, 42.0, 300.0).overscan(6),
offset: 0.0,
}
}
}
impl Render for Example {
fn render(&mut self, cx: &mut Context<Self>) -> Element {
let themes = default_theme(cx.environment());
let theme = themes.resolve(cx.environment().color_scheme);
self.list
.build("records", self.offset, |index| {
Element::text(format!("Record #{:05}", index + 1))
.text_style(TextStyle {
color: theme.foreground,
..TextStyle::default()
})
.height(length(42.0))
.padding(Sides::length(10.0))
})
.width(percent(1.0))
.height(length(300.0))
.background(theme.background)
.on(cx.listener(EventType::Scroll, |app, event, cx| {
if let UiEventKind::Scrolled { offset, .. } = event.kind {
app.offset = offset.y;
cx.notify();
}
}))
}
}
Starting this lesson’s WebAssembly module…