Data List
A semantic label/value list with a shared horizontal label column or a stacked vertical layout
A semantic label/value list for metadata such as account details, order summaries, and configuration values. Horizontal orientation lays every row out in one shared two-column table, so labels align across all items; vertical orientation stacks each label above its value.
When to use this
- Account or profile metadata: Name, email, company, and similar pairs
- Order and record summaries: Ids, dates, totals, and statuses
- Configuration readouts: Setting names with their current values
- Definition-style content: Any display-only term/description pairing
Basic implementation
Basic implementation
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
class DataListExample extends StatelessWidget {
const DataListExample({super.key});
@override
Widget build(BuildContext context) {
return RemixDataList(
semanticLabel: 'Account details',
items: const [
RemixDataListItem(label: 'Name', value: 'Leo Farias'),
RemixDataListItem(label: 'Email', value: 'leo@example.com'),
RemixDataListItem(label: 'Company', value: 'Bitwild'),
],
style: style,
);
}
DataListStyler get style {
return DataListStyler()
.minLabelWidth(120)
.columnSpacing(24)
.rowSpacing(12)
.labelColor(Colors.grey);
}
}Fortal recipe
FortalDataList maps the pinned Radix Themes 3.3 size typography, root row and
column gaps, vertical label/value gap, 120 logical-pixel horizontal label
minimum, and normal/high-contrast label roles.
Fortal data list
import 'package:remix/remix.dart';
import 'ui/ui.dart';
const accountDetails = FortalDataList(
size: FortalDataListSize.size2,
highContrast: true,
semanticLabel: 'Account details',
items: [
RemixDataListItem(label: 'Name', value: 'Leo Farias'),
RemixDataListItem(label: 'Email', value: 'leo@example.com'),
],
);Orientation and narrow widths
The constructor's orientation always wins over styles, and the renderer
never switches orientation on its own. In a bounded horizontal list, string
values can shrink to their widest Unicode grapheme cluster. Long identifiers
without spaces then wrap at grapheme-safe character boundaries instead of
overflowing, including under text scaling and RTL; the accessible value stays
exactly as supplied. Custom value widgets retain their normal minimum intrinsic
width.
The narrowest supported width is the resolved label column minimum (the greater
of minLabelWidth and every label's minimum intrinsic width), plus
columnSpacing, plus the greatest value minimum across the rows. For a string
that minimum is its widest grapheme cluster; for a custom child it is the
child's intrinsic minimum. Below that structural bound, rebuild with
Axis.vertical.
Caller-owned vertical fallback
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
class ResponsiveDataListExample extends StatelessWidget {
const ResponsiveDataListExample({super.key});
static const items = [
RemixDataListItem(label: 'Name', value: 'Leo Farias'),
RemixDataListItem(label: 'Email', value: 'leo@example.com'),
];
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final vertical = constraints.maxWidth < 240;
return RemixDataList(
orientation: vertical ? Axis.vertical : Axis.horizontal,
items: items,
style: DataListStyler()
.minLabelWidth(120)
.columnSpacing(24)
.rowSpacing(12)
.labelValueSpacing(4),
);
},
);
}
}Custom value widgets
An item takes either a string value or one custom child widget. A custom
child inherits the resolved value typography and keeps its own semantics, so
interactive values stay actionable. A display-only child can opt into a
single summarized announcement with semanticValue, which excludes the
child's own semantics entirely — never combine semanticValue with an
interactive child.
Custom value widgets
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
class DataListCustomValueExample extends StatelessWidget {
const DataListCustomValueExample({super.key});
@override
Widget build(BuildContext context) {
return RemixDataList(
items: [
const RemixDataListItem(
label: 'Status',
semanticValue: 'Authorized',
alignment: CrossAxisAlignment.center,
child: RemixBadge(label: 'Authorized'),
),
RemixDataListItem(
label: 'API key',
alignment: CrossAxisAlignment.center,
child: RemixButton(label: 'Reveal key', onPressed: () {}),
),
],
);
}
}Per-item alignment
Each item aligns its label and value cells with start, center, end,
baseline, or stretch. baseline is the default and applies to string
values, where both cells expose a text baseline; two deterministic
adaptations are documented Radix deltas:
- A custom-child row requesting
baselinemaps tostart/top, because an arbitrary widget may expose no text baseline. - Vertical orientation maps
baselinetostart, because stacked cells share no horizontal baseline.
Fortal parity boundaries
Radix exposes compositional Root/Item/Label/Value children plus per-label
color, high-contrast, and width props. Remix keeps its data-driven row model,
one shared label style, and one global highContrast recipe argument. Callers
rebuild with a different size, orientation, alignment, or width at responsive
breakpoints. CSS leading trim and its quarter-em edge offsets are not applied;
Flutter text baselines and untrimmed bounds remain authoritative. Those are
the only visual tolerances—mapped typography, gaps, shared columns, label
minimum, and gray roles remain exact.
Semantics
- The root exposes one
listnode, named bysemanticLabelwhen provided. - Every row is one
listItemnode. A string row announces its label and value from that single node; the visible texts are excluded so nothing is read twice. - A custom child without
semanticValuekeeps its own semantics — including actions — as children of the row node. excludeSemanticsremoves the entire list from the semantics tree.
Flutter has no definition-list role, so the list/list-item pairing is the
documented adaptation of Radix's dl/dt/dd structure.
Constructor
Constructor
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
RemixDataList remixDataListConstructor({
Key? key,
required List<RemixDataListItem> items,
Axis orientation = Axis.horizontal,
String? semanticLabel,
bool excludeSemantics = false,
Style<DataListSpec> style = const DataListStyler.create(),
StyleSpec<DataListSpec>? styleSpec,
}) => throw UnimplementedError();
RemixDataListItem remixDataListItemConstructor({
LocalKey? key,
required String label,
String? value,
Widget? child,
String? semanticValue,
CrossAxisAlignment alignment = CrossAxisAlignment.baseline,
}) => throw UnimplementedError();Properties
Widget Properties
items → List<RemixDataListItem>
Required. The rows to display; may be empty. The list is retained as supplied, so do not mutate it during build — each build reads one immutable snapshot of the current contents.
orientation → Axis
Optional. Layout axis, defaulting to Axis.horizontal. The constructor
always wins; orientation is intentionally absent from DataListSpec.
semanticLabel → String?
Optional. Accessible name announced for the list itself. When provided it must not be whitespace-only (asserted in debug builds).
excludeSemantics → bool
Optional. Removes the entire list, rows included, from semantics. Defaults
to false.
style → Style<DataListSpec>
Optional. The style configuration for typography, cell containers, and layout metrics.
styleSpec → StyleSpec<DataListSpec>?
Optional. A pre-resolved style spec that bypasses style resolution. Useful
for performance when sharing resolved styles across multiple instances. Pass
the resolved data as const StyleSpec(spec: DataListSpec(...)).
key → Key?
Optional. Controls how one widget replaces another widget in the tree.
Item Properties
key → LocalKey?
Optional stable identity for the row. Without a key, rows are matched by
position, so a stateful custom child keeps its state at the old position
after a reorder and can end up attached to the wrong label. Provide a key
whenever items with custom children are reordered, inserted, or removed.
label → String
Required. The visible label, also announced as the row's semantic label. It must be nonempty and not whitespace-only: the constructor rejects the empty string and debug builds additionally reject trimmed-blank text. Displayed text is never trimmed.
value → String?
The string value, rendered with the value typography. Exactly one of
value or child must be provided; a provided value must be nonempty
and not whitespace-only.
child → Widget?
A custom value widget that inherits the resolved value typography and keeps
its own semantics unless semanticValue summarizes it.
semanticValue → String?
Optional noninteractive summary announced instead of the child's semantics.
Valid only together with child; when provided it must be nonempty and not
whitespace-only.
alignment → CrossAxisAlignment
Optional. Cross-cell alignment for this row, defaulting to
CrossAxisAlignment.baseline.
Style Methods
label(TextStyler value) / value(TextStyler value)
Sets the label or value text slot.
labelTextStyle(TextStyleMix style) / valueTextStyle(TextStyleMix style)
Sets the label or value text style.
labelColor(Color color) / valueColor(Color color)
Sets the label or value text color.
labelContainer(BoxStyler value) / valueContainer(BoxStyler value)
Styles the cell containers around the label or value without coupling the text styles.
rowSpacing(double value)
Sets the gap between items along the list's main axis.
columnSpacing(double value)
Sets the directional horizontal gap between the label and value columns.
labelValueSpacing(double value)
Sets the inner vertical gap between label and value in vertical orientation.
minLabelWidth(double value)
Sets the minimum width of the shared horizontal label column.
padding(EdgeInsetsGeometryMix value) / margin(EdgeInsetsGeometryMix value)
Sets outer container padding or margin.
color(Color value) / decoration(DecorationMix value)
Sets the outer container background or decoration.
wrap(WidgetModifierConfig value)
Applies widget modifiers such as clipping, opacity, or scaling.
animate(AnimationConfig value)
Configures implicit animation for style transitions.
call({Key? key, required List<RemixDataListItem> items, Axis orientation, String? semanticLabel, bool excludeSemantics})
Creates a RemixDataList widget with this style applied.
See the alignment API migration table for source-breaking type replacements.