Skip to content

WTabs

View raw .md

Controlled tabs widget. Renders a tab list and a content panel. The selected: state prefix activates on the currently active tab, so callers can supply tokens like selected:border-b-2 selected:text-blue-600 through tabClassName. This is a fully controlled widget: selectedIndex is managed by the caller.

On this page

Basic Usage#

Preview widgets/w_tabs_basic The live demo is not available for this example. w_tabs_basic.dart View source
dart
WTabs(
  tabs: const ['Overview', 'Details', 'Settings'],
  selectedIndex: _selectedTab,
  onChanged: (i) => setState(() => _selectedTab = i),
  listClassName: 'flex flex-row border-b border-gray-200 dark:border-gray-700',
  tabClassName: '''
    px-4 py-2 text-sm text-gray-500 dark:text-gray-400
    selected:text-blue-600 dark:selected:text-blue-400
    selected:border-b-2 selected:border-blue-600 dark:selected:border-blue-400
  ''',
  panelClassName: 'pt-4',
  panelBuilder: (index) => _panels[index],
)

Props#

Prop Type Default Description
tabs List<String> Required The labels rendered for each tab, in display order.
selectedIndex int Required Zero-based index of the currently selected tab.
panelBuilder Widget Function(int index) Required Returns the panel content for the currently selected tab.
onChanged ValueChanged<int>? null Called with the tapped tab's index. The caller updates selectedIndex in response.
listClassName String? null Utility classes for the tab-list row container. Example: 'flex flex-row border-b border-gray-200 dark:border-gray-700'.
tabClassName String? null Utility classes applied to every tab's inner WDiv. Supports selected: prefixed tokens: they activate only on the selected tab.
selectedTabClassName String? null Extra utility classes appended to the active tab's WDiv only. Applied after tabClassName.
panelClassName String? null Utility classes for the panel wrapper WDiv. Example: 'pt-4'.
fullWidthList bool true When true, prepends w-full to the tab list so a border-b baseline spans the full container width (tabs stay content-sized, left-aligned). If listClassName already sets a width token (w-*), that width is respected and w-full is not prepended, so an explicit width wins. Set false for a content-width strip (centered pill tabs).

Constructor#

dart
WTabs({
  Key? key,
  required List<String> tabs,
  required int selectedIndex,
  required Widget Function(int index) panelBuilder,
  ValueChanged<int>? onChanged,
  String? listClassName,
  String? tabClassName,
  String? selectedTabClassName,
  String? panelClassName,
  bool fullWidthList = true,
})

Styling Examples#

Underline Tabs#

Classic underline style: selected:border-b-2 on the active tab. Pair border and text colors with dark: variants:

dart
WTabs(
  tabs: const ['Overview', 'Activity', 'Settings'],
  selectedIndex: _tab,
  onChanged: (i) => setState(() => _tab = i),
  listClassName: 'flex flex-row border-b border-gray-200 dark:border-gray-700',
  tabClassName: '''
    px-4 py-3 text-sm font-medium
    text-gray-500 dark:text-gray-400
    selected:text-rose-600 dark:selected:text-rose-400
    selected:border-b-2 selected:border-rose-600 dark:selected:border-rose-400
  ''',
  panelClassName: 'pt-4',
  panelBuilder: (index) => panels[index],
)

Pill / Segmented Control#

Background-based active tab with rounded container:

dart
WTabs(
  tabs: const ['All', 'Open', 'Resolved'],
  selectedIndex: _tab,
  onChanged: (i) => setState(() => _tab = i),
  listClassName: 'flex flex-row gap-1 p-1 rounded-lg bg-gray-100 dark:bg-gray-800',
  tabClassName: '''
    px-3 py-1.5 rounded-md text-sm font-medium
    text-gray-600 dark:text-gray-400
    selected:bg-white dark:selected:bg-gray-700
    selected:text-gray-900 dark:selected:text-white
    selected:shadow-sm
  ''',
  panelClassName: 'pt-4',
  panelBuilder: (index) => panels[index],
)

selectedTabClassName for Active-Only Tokens#

Use selectedTabClassName to append active-only classes without selected: prefixes:

dart
WTabs(
  tabs: const ['Profile', 'Security', 'Billing'],
  selectedIndex: _tab,
  onChanged: (i) => setState(() => _tab = i),
  tabClassName: 'px-4 py-2 text-sm text-gray-500 dark:text-gray-400',
  selectedTabClassName: 'text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400',
  panelBuilder: (index) => panels[index],
)
  • WAnchor: each tab is wrapped in a WAnchor providing hover and focus state.
  • WDiv: renders the tab list and panel containers.
  • WCheckbox: another controlled widget that drives state prefixes from a prop.
esc

Search index did not load.

No match for "".

Try a class like rounded-lg or a widget like WButton.