RemixRemix
Data display

Data List

A semantic label/value list with a shared horizontal label column or a stacked vertical layout

Open catalog
Starting Flutter example…
Live Flutter from this checkout’s component catalog. Examples include Fortal styling; the source tab shows the catalog code, not a standalone application.

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 baseline maps to start/top, because an arbitrary widget may expose no text baseline.
  • Vertical orientation maps baseline to start, 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 list node, named by semanticLabel when provided.
  • Every row is one listItem node. 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 semanticValue keeps its own semantics — including actions — as children of the row node.
  • excludeSemantics removes 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.

View page source on GitHub

On this page