Skip to content

Query Controls, Animation, Keys & Progress

A handful of small standalone widgets and helpers that don't warrant their own pages.

Live examples

Open the full Feedback and Progress example.

ChoiceChipGroup

Single-select chip strip used by query-driven view controls. The underlying class is ChoiceChipGroup<T>.

dart
ChoiceChipGroup<TaskStatus>(
  options: const [
    ChipOption(value: TaskStatus.pending, label: 'Pending'),
    ChipOption(value: TaskStatus.approved, label: 'Approved'),
    ChipOption(value: TaskStatus.rejected, label: 'Rejected'),
  ],
  selected: filter.status,
  onSelected: (s) => filter.setStatus(s),         // null when re-tapping the active chip
  leadingIcon: FluentIcons.filter_24_regular,
  semanticKey: const SemanticKey('inbox.filter.status'),
)

Tapping the already-selected chip deselects it (passes null).

Beacon

Attention-grabbing pulse indicator. Two visual modes:

ModeTriggered byUse
Ringnon-null iconTimeline pending nodes — circle with icon, animated outer ring
Glow doticon == nullStatus indicators — glowing dot with expanding ripple
dart
// Pending timeline indicator
Beacon(
  icon: FluentIcons.hourglass_24_regular,
  color: theme.colorScheme.primary,
  size: 32,
  iconSize: 16,
  pulsing: true,
)

// Onboarding "look here" dot
Beacon(
  color: Colors.red,
  size: 8,
  pulsing: true,
)

// Static (non-animated) variant — faint outer circle instead of pulse
Beacon(
  icon: FluentIcons.warning_24_regular,
  color: cdxColors.warning,
  pulsing: false,
)

SemanticKey

Structured widget identification for automation, testing, and analytics. Composes dot-separated hierarchical paths.

dart
abstract final class InboxKeys {
  static const dashboard = SemanticKey('inbox.dashboard');
}

// Compose
final searchKey = (InboxKeys.dashboard / 'field' / 'search').value;
// → ValueKey('inbox.dashboard.field.search')

TextField(key: searchKey, ...)

Naming convention: screen.widgetType.name using camelCase within each segment and dots as separators (dots cannot appear in Dart identifiers, avoiding delimiter collisions).

Progress

Two themed progress indicators with sensible defaults.

CircularProgress

dart
CircularProgress()                                         // 24 px indeterminate
CircularProgress(size: 16, strokeWidth: 2)                 // small inline spinner
CircularProgress(value: 0.75)                              // determinate
CircularProgress(color: Colors.green, trackColor: Colors.green.shade100)

Defaults: 24 px, stroke 3, color = colorScheme.primary, track = colorScheme.surfaceContainerHighest.

LineProgress

dart
LineProgress()                                             // indeterminate, 3 px tall pill
LineProgress(value: 0.6)                                   // determinate
LineProgress(height: 4, borderRadius: 2)                   // square edges
LineProgress(width: 200)                                   // bounded width

Defaults: 3 px tall, full width, pill shape (height / 2 radius), color = colorScheme.primary.

  • ButtonsButton.toggle for boolean filter chips
  • State ViewsLoadingState wraps CircularProgress
  • CardsHeroCard uses LinearProgressIndicator internally for its loading line