Skip to Content

A toggle group presents a compact set of mutually exclusive options. Only one item is in the Tab order; arrow keys move focus without changing selection.

Interactive preview

Resolving preview metadata...

When to use this

  • View switching: Choose list, grid, or board presentation
  • Formatting choices: Pick one alignment or text mode
  • Compact filters: Select one option from a short, visible set
  • Toolbar controls: Preserve focus-only arrow navigation until activation

Basic implementation

Basic implementation
class ViewToggle extends StatefulWidget { const ViewToggle({super.key}); @override State<ViewToggle> createState() => _ViewToggleState(); } class _ViewToggleState extends State<ViewToggle> { String _value = 'list'; @override Widget build(BuildContext context) { return FortalToggleGroup<String>( items: const [ RemixToggleGroupItem( value: 'list', label: 'List', icon: Icons.view_list, ), RemixToggleGroupItem( value: 'grid', label: 'Grid', icon: Icons.grid_view, ), RemixToggleGroupItem( value: 'board', label: 'Board', icon: Icons.view_kanban, ), ], selectedValue: _value, onChanged: (value) { if (value != null) setState(() => _value = value); }, semanticLabel: 'View style', ); } }

Keyboard behavior

  • Tab enters on the selected item, or the first enabled item when none is selected
  • A second Tab leaves the group instead of visiting every item
  • Arrow Left / Arrow Right move focus in horizontal groups
  • Arrow Up / Arrow Down move focus in vertical groups
  • Home / End move focus to the first / last enabled item
  • Space / Enter activate the focused item
  • Arrow movement wraps when loop is true and clamps when false
  • Horizontal arrow direction follows the ambient text direction in RTL layouts

Arrow, Home, and End keys move focus only. Selection changes after explicit activation with Space, Enter, or a pointer.

Value and enabled-state contract

  • Item values are non-null, unique keys with stable equality and hash codes
  • selectedValue: null means no selection; otherwise it must match one item
  • An empty items list is valid when selectedValue is null
  • At most one item may set autofocus: true
  • onChanged: null disables the entire group, just like enabled: false

Rebuild with a new item list when values or ordering change. Do not mutate the list or a value’s equality behavior while it is rendered.

Visible text supplies an item’s accessible name. Icon-only items must provide a non-empty semanticLabel; an explicit semantic label replaces the visible label for accessibility services instead of being announced in addition to it.

Per-item styling

The group’s item style is the default for every option. An item’s style is merged afterward, so one option can add a local treatment while retaining group states and sizing.

The group styler controls structural container styling and context variants such as brightness, breakpoints, and text direction. Put hover, press, focus, selected, and disabled variants on RemixToggleGroupItemStyler; they resolve independently for each option. A raw styleSpec is authoritative and bypasses both the group and per-item fluent stylers.

Per-item style
RemixToggleGroup<String>( items: [ const RemixToggleGroupItem(value: 'draft', label: 'Draft'), RemixToggleGroupItem( value: 'published', label: 'Published', style: RemixToggleGroupItemStyler().foregroundColor(Colors.green), ), ], selectedValue: value, onChanged: onChanged, style: RemixToggleGroupStyler().item( RemixToggleGroupItemStyler() .paddingX(12) .paddingY(8) .onSelected(.color(Colors.green.shade50)), ), )

Constructor

RemixToggleGroup
const RemixToggleGroup<T>({ Key? key, required List<RemixToggleGroupItem<T>> items, required T? selectedValue, ValueChanged<T?>? onChanged, bool enabled = true, Axis orientation = Axis.horizontal, bool loop = true, String? semanticLabel, bool excludeSemantics = false, RemixToggleGroupStyler style = const RemixToggleGroupStyler.create(), RemixToggleGroupSpec? styleSpec, })
RemixToggleGroupItem
const RemixToggleGroupItem<T>({ required T value, String? label, IconData? icon, String? semanticLabel, bool enabled = true, FocusNode? focusNode, bool autofocus = false, RemixToggleGroupItemStyler style = const RemixToggleGroupItemStyler.create(), })

Fortal widgets

FortalToggleGroup provides soft and surface variants plus three sizes. The recipe uses edge-to-edge items, a bordered surface container, selected accent colors, a visible keyboard focus ring, and disabled state styling.

See the fortalToggleGroupStyler source code  for all available options.

Last updated on