VisorVisor
FlutterWidgets

VisorEmptyStateCard

Card-chrome wrapper around VisorEmptyState for drop-in use inside lists, panels, and slotted containers. Always uses the compact horizontal layout.

VisorEmptyStateCard wraps VisorEmptyState in a card surface and forces the compact horizontal layout. It's the right choice when an empty state needs to live inline — inside a list slot, a half-height bottom sheet, or a dashboard panel — instead of taking over the full viewport.

Preview

When to Use

  • Inserting an empty state directly into a scrollable list slot
  • Height-constrained panels (bottom sheets, half-height drawers, inline dashboard cards)
  • Any context where the card surface should be self-contained and draggable

When Not to Use

  • Full-page empty states where a standard vertical VisorEmptyState reads better
  • Inside an existing card surface (nested card chrome looks wrong)
  • Loading states (use Skeleton or a spinner)

Installation

npx visor add empty-state-card --target flutter

Or copy components/flutter/visor_empty_state_card/visor_empty_state_card.dart into your project.

Basic Usage

import 'package:ui/ui.dart';

VisorEmptyStateCard(
  icon: Icons.inbox_outlined,
  headline: 'No messages yet',
  body: "You're all caught up.",
)

With Actions

VisorEmptyStateCard(
  icon: Icons.folder_open,
  headline: 'No projects',
  body: 'Create your first project to get started.',
  action: VisorButton(
    label: 'Create project',
    onPressed: _createProject,
  ),
  secondaryAction: VisorButton(
    label: 'Import',
    style: VisorButtonStyle.secondary,
    onPressed: _importProject,
  ),
)

API Reference

PropertyTypeDefaultDescription
iconIconDatarequiredIcon rendered to the left of the text content
headlineStringrequiredPrimary label — short, imperative, scannable
bodyString?nullSupporting copy explaining why the state is empty
actionWidget?nullPrimary call-to-action widget
secondaryActionWidget?nullOptional secondary action
iconSizedouble48Logical-pixel size of the icon (scaled to 62.5% in compact layout)
semanticLabelString?nullOverride the announced label, forwarded to inner VisorEmptyState

Accessibility

  • Semantics are provided by the inner VisorEmptyState — the card chrome itself does not duplicate the announcement.
  • Layout uses EdgeInsetsDirectional so RTL locales mirror correctly.

Source

  • components/flutter/visor_empty_state_card/visor_empty_state_card.dart
  • Quality contract audit row: docs/flutter-widget-quality-contract.md (Rec8)