chico_ui 1.7.0 copy "chico_ui: ^1.7.0" to clipboard
chico_ui: ^1.7.0 copied to clipboard

Zero-dependency Flutter UI kit for minimal system interfaces. Responsive, adaptive, RTL-first widgets built from design tokens.

example/lib/main.dart

import 'package:chico_ui/chico_ui.dart';
import 'package:flutter/widgets.dart';

void main() {
  runApp(const GalleryApp());
}

/// Live catalog of Chico UI. Rebuilds [ChicoApp] when the appearance mode
/// changes so light / dark / system can be compared on device.
class GalleryApp extends StatefulWidget {
  const GalleryApp({super.key});

  @override
  State<GalleryApp> createState() => _GalleryAppState();
}

class _GalleryAppState extends State<GalleryApp> {
  ChicoThemeMode _mode = ChicoThemeMode.system;
  Locale? _locale;

  @override
  Widget build(BuildContext context) {
    return ChicoApp(
      title: 'Chico UI',
      themeMode: _mode,
      locale: _locale,
      home: GalleryPage(
        mode: _mode,
        locale: _locale,
        onModeChanged: (mode) => setState(() => _mode = mode),
        onLocaleChanged: (locale) => setState(() => _locale = locale),
      ),
    );
  }
}

/// Scrollable catalog of every Chico widget.
class GalleryPage extends StatelessWidget {
  const GalleryPage({
    super.key,
    required this.mode,
    required this.locale,
    required this.onModeChanged,
    required this.onLocaleChanged,
  });

  final ChicoThemeMode mode;
  final Locale? locale;
  final ValueChanged<ChicoThemeMode> onModeChanged;
  final ValueChanged<Locale?> onLocaleChanged;

  @override
  Widget build(BuildContext context) {
    final theme = ChicoTheme.of(context);
    final media = ChicoMedia.of(context);

    return ChicoPage(
      title: 'Chico UI',
      child: ChicoColumn(
        gap: ChicoSpace.space24,
        children: [
          const ChicoText(
            'Minimal system kit. Resize the window or rotate the phone '
            'to see breakpoints change. Tap outside a field to dismiss '
            'the keyboard.',
            role: ChicoTextRole.secondary,
          ),
          _AppearanceRow(mode: mode, onModeChanged: onModeChanged),
          _LocaleRow(locale: locale, onLocaleChanged: onLocaleChanged),
          const _SectionTitle('Window'),
          _WindowCard(media: media, theme: theme),
          const _SectionTitle('Actions'),
          ChicoRow(
            gap: ChicoSpace.space8,
            wrapBelow: ChicoBreakpoint.sm,
            children: [
              ChicoButton(label: 'Continue', onPressed: () {}),
              ChicoButton(
                label: 'Cancel',
                tone: ChicoButtonTone.neutral,
                onPressed: () {},
              ),
              ChicoButton(
                label: 'Delete',
                tone: ChicoButtonTone.destructive,
                onPressed: () {},
              ),
            ],
          ),
          const ChicoButton(label: 'Disabled', expand: true),
          ChicoRow(
            gap: ChicoSpace.space8,
            wrap: true,
            children: [
              ChicoIconButton(
                icon: const ChicoGlyph(ChicoGlyphKind.add),
                tooltip: 'Add',
                tone: ChicoButtonTone.accent,
                onPressed: () {},
              ),
              ChicoIconButton(
                icon: const ChicoGlyph(ChicoGlyphKind.close),
                tooltip: 'Close',
                onPressed: () {},
              ),
              ChicoIconButton(
                icon: const ChicoGlyph(ChicoGlyphKind.check),
                tooltip: 'Done',
                onPressed: () {},
              ),
              ChicoIconButton(
                icon: const ChicoGlyph(ChicoGlyphKind.chevronBack),
                tooltip: 'Back',
                onPressed: () {},
              ),
              ChicoIconButton(
                icon: const ChicoGlyph(ChicoGlyphKind.home),
                tooltip: 'Home',
                onPressed: () {},
              ),
              ChicoIconButton(
                icon: const ChicoGlyph(ChicoGlyphKind.search),
                tooltip: 'Search',
                onPressed: () {},
              ),
              ChicoIconButton(
                icon: const ChicoGlyph(ChicoGlyphKind.person),
                tooltip: 'You',
                onPressed: () {},
              ),
            ],
          ),
          ChicoLink(label: 'Learn more', onPressed: () {}),
          ChicoRow(
            gap: ChicoSpace.space8,
            wrap: true,
            children: [
              for (final kind in <ChicoGlyphKind>[
                ChicoGlyphKind.settings,
                ChicoGlyphKind.trash,
                ChicoGlyphKind.mail,
                ChicoGlyphKind.lock,
                ChicoGlyphKind.eye,
                ChicoGlyphKind.calendar,
                ChicoGlyphKind.star,
                ChicoGlyphKind.bell,
              ])
                ChicoIconButton(
                  icon: ChicoGlyph(kind),
                  tooltip: kind.name,
                  onPressed: () {},
                ),
            ],
          ),
          const _SectionTitle('Fields'),
          const ChicoText(
            'Switch keyboard language while typing. Tap outside to unfocus.',
            role: ChicoTextRole.secondary,
            variant: ChicoTextVariant.footnote,
          ),
          const ChicoTextField(label: 'Name', hint: 'Type English or العربية…'),
          const ChicoTextField(
            label: 'Password',
            hint: '••••••••',
            obscureText: true,
          ),
          const ChicoTextField(
            label: 'Amount',
            hint: '0.00',
            leading: ChicoText('USD', role: ChicoTextRole.secondary),
            maxLength: 8,
            keyboardType: TextInputType.number,
          ),
          const _SelectPlayground(),
          const _ValidatePlayground(),
          const _FormsPlayground(),
          const _SectionTitle('Inputs+'),
          const _InputsPlayground(),
          const _SectionTitle('Feedback'),
          const _FeedbackPlayground(),
          const _SectionTitle('Surfaces'),
          const _SurfacesPlayground(),
          const _SectionTitle('Lists & grids'),
          const _ListsPlayground(),
          const _SectionTitle('Content+'),
          const _ContentPlayground(),
          const _SectionTitle('Navigation'),
          const _NavigationPlayground(),
          const _SectionTitle('Navigation+'),
          const _NavigationPlusPlayground(),
          const _SectionTitle('Adaptive'),
          const _AdaptivePlayground(),
          const _SectionTitle('Overlays'),
          const _OverlaysPlayground(),
          const _SectionTitle('Sheets'),
          const _SheetsPlayground(),
          const _SectionTitle('Pickers'),
          const _PickersPlayground(),
          const _SectionTitle('Data+'),
          const _DataPlayground(),
          const _SectionTitle('Type scale'),
          ...ChicoTextVariant.values.map((variant) {
            return ChicoText(variant.name, variant: variant);
          }),
          const _SectionTitle('Text roles'),
          const ChicoText('label — primary copy'),
          const ChicoText(
            'secondary — subtitles, timestamps',
            role: ChicoTextRole.secondary,
          ),
          const ChicoText(
            'tertiary — hints, metadata',
            role: ChicoTextRole.tertiary,
          ),
          const ChicoText('tint — links, selection', role: ChicoTextRole.tint),
          const ChicoText(
            'destructive — errors, delete',
            role: ChicoTextRole.destructive,
          ),
          const ChicoText(
            'success — saved, online',
            role: ChicoTextRole.success,
          ),
          const ChicoText(
            'warning — pending, caution',
            role: ChicoTextRole.warning,
          ),
          const _SectionTitle('Colors'),
          _ColorGrid(theme: theme, media: media),
          const _SectionTitle('Spacing'),
          _SpacingScale(theme: theme, media: media),
          const _SectionTitle('Components'),
          const ChicoText(
            'Everything in the kit, grouped by category.',
            role: ChicoTextRole.secondary,
          ),
          ..._roadmap.map(
            (item) => _RoadmapRow(item: item, theme: theme, media: media),
          ),
        ],
      ),
    );
  }
}

class _SectionTitle extends StatelessWidget {
  const _SectionTitle(this.label);

  final String label;

  @override
  Widget build(BuildContext context) {
    final media = ChicoMedia.of(context);
    return Padding(
      padding: EdgeInsets.only(bottom: media.space(ChicoSpace.space8)),
      child: ChicoText(label, variant: ChicoTextVariant.headline),
    );
  }
}

class _AppearanceRow extends StatelessWidget {
  const _AppearanceRow({required this.mode, required this.onModeChanged});

  final ChicoThemeMode mode;
  final ValueChanged<ChicoThemeMode> onModeChanged;

  @override
  Widget build(BuildContext context) {
    final media = ChicoMedia.of(context);
    return Wrap(
      spacing: media.space(ChicoSpace.space8),
      runSpacing: media.space(ChicoSpace.space8),
      children: [
        for (final value in ChicoThemeMode.values)
          _ModeChip(
            label: value.name,
            selected: mode == value,
            onTap: () => onModeChanged(value),
          ),
      ],
    );
  }
}

class _ModeChip extends StatelessWidget {
  const _ModeChip({
    required this.label,
    required this.selected,
    required this.onTap,
  });

  final String label;
  final bool selected;
  final VoidCallback onTap;

  @override
  Widget build(BuildContext context) {
    final theme = ChicoTheme.of(context);
    final fill = selected ? theme.colors.tint : theme.colors.fill;
    final foreground = selected ? theme.colors.onTint : theme.colors.label;

    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTap: onTap,
      child: ConstrainedBox(
        constraints: const BoxConstraints(minHeight: ChicoHitTarget.min),
        child: DecoratedBox(
          decoration: BoxDecoration(
            color: fill,
            borderRadius: ChicoRadius.borderMd,
          ),
          child: Padding(
            padding: const EdgeInsets.symmetric(
              horizontal: ChicoSpace.space16,
              vertical: ChicoSpace.space8,
            ),
            child: Center(
              child: ChicoText(
                label,
                variant: ChicoTextVariant.subheadline,
                color: foreground,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class _LocaleRow extends StatelessWidget {
  const _LocaleRow({required this.locale, required this.onLocaleChanged});

  final Locale? locale;
  final ValueChanged<Locale?> onLocaleChanged;

  @override
  Widget build(BuildContext context) {
    final media = ChicoMedia.of(context);
    return Wrap(
      spacing: media.space(ChicoSpace.space8),
      runSpacing: media.space(ChicoSpace.space8),
      children: [
        _ModeChip(
          label: 'system',
          selected: locale == null,
          onTap: () => onLocaleChanged(null),
        ),
        _ModeChip(
          label: 'English',
          selected: locale?.languageCode == 'en',
          onTap: () => onLocaleChanged(const Locale('en')),
        ),
        _ModeChip(
          label: 'العربية',
          selected: locale?.languageCode == 'ar',
          onTap: () => onLocaleChanged(const Locale('ar')),
        ),
        _ModeChip(
          label: 'עברית',
          selected: locale?.languageCode == 'he',
          onTap: () => onLocaleChanged(const Locale('he')),
        ),
      ],
    );
  }
}

class _ValidatePlayground extends StatefulWidget {
  const _ValidatePlayground();

  @override
  State<_ValidatePlayground> createState() => _ValidatePlaygroundState();
}

class _ValidatePlaygroundState extends State<_ValidatePlayground> {
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return ChicoForm(
      formKey: _formKey,
      child: ChicoColumn(
        gap: ChicoSpace.space12,
        children: [
          ChicoTextFormField(
            label: 'Email',
            hint: 'ada@example.com',
            keyboardType: TextInputType.emailAddress,
            validator: (value) {
              if (value == null || !value.contains('@')) {
                return 'Enter an email';
              }
              return null;
            },
          ),
          ChicoButton(
            label: 'Validate',
            expand: true,
            tone: ChicoButtonTone.neutral,
            onPressed: () => _formKey.currentState?.validate(),
          ),
        ],
      ),
    );
  }
}

class _FormsPlayground extends StatefulWidget {
  const _FormsPlayground();

  @override
  State<_FormsPlayground> createState() => _FormsPlaygroundState();
}

class _FormsPlaygroundState extends State<_FormsPlayground> {
  var _agreed = true;
  var _notify = false;
  var _size = 1;
  var _volume = 0.4;
  var _low = 0.2;
  var _high = 0.8;
  var _stars = 4;

  @override
  Widget build(BuildContext context) {
    return ChicoColumn(
      gap: ChicoSpace.space12,
      children: [
        ChicoCheckbox(
          value: _agreed,
          label: 'I agree to the terms',
          onChanged: (value) => setState(() => _agreed = value),
        ),
        ChicoSwitch(
          value: _notify,
          label: 'Notifications',
          onChanged: (value) => setState(() => _notify = value),
        ),
        ChicoRadio<int>(
          value: 0,
          groupValue: _size,
          label: 'Small',
          onChanged: (value) => setState(() => _size = value),
        ),
        ChicoRadio<int>(
          value: 1,
          groupValue: _size,
          label: 'Medium',
          onChanged: (value) => setState(() => _size = value),
        ),
        ChicoRadio<int>(
          value: 2,
          groupValue: _size,
          label: 'Large',
          onChanged: (value) => setState(() => _size = value),
        ),
        ChicoText(
          'Volume ${(_volume * 100).round()}%',
          variant: ChicoTextVariant.footnote,
          role: ChicoTextRole.secondary,
        ),
        ChicoSlider(
          value: _volume,
          onChanged: (value) => setState(() => _volume = value),
        ),
        ChicoText(
          'Range ${(_low * 100).round()}–${(_high * 100).round()}',
          variant: ChicoTextVariant.footnote,
          role: ChicoTextRole.secondary,
        ),
        ChicoRangeSlider(
          start: _low,
          end: _high,
          onChanged: (start, end) => setState(() {
            _low = start;
            _high = end;
          }),
        ),
        ChicoRating(
          value: _stars,
          onChanged: (value) => setState(() => _stars = value),
        ),
        const ChicoAutocomplete(
          label: 'Name',
          hint: 'Ada, Alan, Grace…',
          options: ['Ada', 'Alan', 'Grace', 'Linus', 'Margaret'],
        ),
        const ChicoPinField(length: 4),
        const ChicoNumberField(
          label: 'Amount',
          style: ChicoNumberStyle.currency,
          hint: '0.00',
        ),
      ],
    );
  }
}

class _InputsPlayground extends StatefulWidget {
  const _InputsPlayground();

  @override
  State<_InputsPlayground> createState() => _InputsPlaygroundState();
}

class _InputsPlaygroundState extends State<_InputsPlayground> {
  var _unread = true;
  var _flagged = false;
  var _work = true;
  var _count = 2;
  var _tags = <String>['Design'];

  @override
  Widget build(BuildContext context) {
    return ChicoColumn(
      gap: ChicoSpace.space12,
      children: [
        ChicoSearch(hint: 'Mail, people, documents', onChanged: (_) {}),
        Wrap(
          spacing: ChicoSpace.space8,
          runSpacing: ChicoSpace.space8,
          children: [
            ChicoChip(
              label: 'Unread',
              selected: _unread,
              onPressed: () => setState(() => _unread = !_unread),
            ),
            ChicoChip(
              label: 'Flagged',
              selected: _flagged,
              onPressed: () => setState(() => _flagged = !_flagged),
            ),
            if (_work)
              ChicoChip(
                label: 'Work',
                onDeleted: () => setState(() => _work = false),
              ),
          ],
        ),
        ChicoTokenField(
          label: 'Tags',
          hint: 'Add a tag',
          tokens: _tags,
          onChanged: (next) => setState(() => _tags = next),
        ),
        ChicoRow(
          gap: ChicoSpace.space12,
          children: [
            const ChicoText('Quantity'),
            ChicoStepper(
              value: _count,
              min: 1,
              max: 9,
              onChanged: (value) => setState(() => _count = value),
            ),
          ],
        ),
      ],
    );
  }
}

class _FeedbackPlayground extends StatefulWidget {
  const _FeedbackPlayground();

  @override
  State<_FeedbackPlayground> createState() => _FeedbackPlaygroundState();
}

class _FeedbackPlaygroundState extends State<_FeedbackPlayground> {
  var _showBanner = true;
  var _progress = 0.45;

  @override
  Widget build(BuildContext context) {
    return ChicoColumn(
      gap: ChicoSpace.space12,
      children: [
        const ChicoRow(
          gap: ChicoSpace.space16,
          children: [
            ChicoSpinner(),
            ChicoSpinner(tone: ChicoButtonTone.neutral),
            ChicoSpinner(tone: ChicoButtonTone.destructive),
          ],
        ),
        ChicoText(
          'Upload ${(_progress * 100).round()}%',
          variant: ChicoTextVariant.footnote,
          role: ChicoTextRole.secondary,
        ),
        ChicoProgress(value: _progress),
        ChicoSlider(
          value: _progress,
          onChanged: (value) => setState(() => _progress = value),
        ),
        const ChicoText(
          'Indeterminate',
          variant: ChicoTextVariant.footnote,
          role: ChicoTextRole.secondary,
        ),
        const ChicoProgress(),
        if (_showBanner)
          ChicoBanner(
            title: 'Saved',
            message: 'A copy is on this device.',
            actionLabel: 'Undo',
            onAction: () {},
            onDismiss: () => setState(() => _showBanner = false),
          ),
        const ChicoBanner(
          message: 'Could not reach the server.',
          tone: ChicoButtonTone.destructive,
        ),
        const ChicoBanner(
          title: 'Synced',
          message: 'All changes are on this device.',
          tone: ChicoButtonTone.success,
        ),
        const ChicoBanner(
          message: 'Battery is low.',
          tone: ChicoButtonTone.warning,
        ),
        const ChicoSkeleton.tile(),
        ChicoButton(
          label: 'Show dialog',
          expand: true,
          onPressed: () {
            ChicoDialog.show<bool>(
              context,
              title: 'Delete item?',
              message: 'This cannot be undone.',
              actions: const [
                ChicoDialogAction(
                  label: 'Cancel',
                  result: false,
                  tone: ChicoButtonTone.neutral,
                ),
                ChicoDialogAction(
                  label: 'Delete',
                  result: true,
                  tone: ChicoButtonTone.destructive,
                  isDefault: true,
                ),
              ],
            );
          },
        ),
        ChicoButton(
          label: 'Show prompt',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () {
            ChicoDialog.prompt(context, title: 'Rename', hint: 'Name');
          },
        ),
        ChicoButton(
          label: 'Show confirm',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () {
            ChicoDialog.confirm(
              context,
              title: 'Delete item?',
              message: 'This cannot be undone.',
              confirmTone: ChicoButtonTone.destructive,
            );
          },
        ),
        ChicoButton(
          label: 'Show busy',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () async {
            ChicoBusy.show(context, message: 'Saving');
            await Future<void>.delayed(const Duration(milliseconds: 1200));
            ChicoBusy.hide();
          },
        ),
        ChicoButton(
          label: 'Push page',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () {
            Navigator.of(context).push(
              ChicoPageRoute<void>(
                builder: (context) {
                  return ChicoScaffold(
                    bar: const ChicoBar(title: 'Detail'),
                    body: const ChicoPage(
                      child: ChicoText('Pushed with ChicoPageRoute.'),
                    ),
                  );
                },
              ),
            );
          },
        ),
      ],
    );
  }
}

class _SurfacesPlayground extends StatelessWidget {
  const _SurfacesPlayground();

  @override
  Widget build(BuildContext context) {
    return ChicoColumn(
      gap: ChicoSpace.space12,
      children: [
        ChicoRow(
          gap: ChicoSpace.space12,
          children: [
            ChicoAvatar.name('Ada Lovelace'),
            ChicoAvatar.name('Grace Hopper', size: 32),
            ChicoBadge(count: 3, child: ChicoAvatar.name('Alan Turing')),
            const ChicoBadge(count: 12),
            const ChicoBadge(tone: ChicoButtonTone.destructive),
          ],
        ),
        ChicoAvatarStack(
          avatars: [
            ChicoAvatar.name('Ada Lovelace', size: 32),
            ChicoAvatar.name('Grace Hopper', size: 32),
            ChicoAvatar.name('Alan Turing', size: 32),
            ChicoAvatar.name('Linus Torvalds', size: 32),
          ],
        ),
        const ChicoCard(
          child: ChicoText(
            'Grouped surface. Hairline, no shadow.',
            role: ChicoTextRole.secondary,
          ),
        ),
      ],
    );
  }
}

class _ListsPlayground extends StatefulWidget {
  const _ListsPlayground();

  @override
  State<_ListsPlayground> createState() => _ListsPlaygroundState();
}

class _ListsPlaygroundState extends State<_ListsPlayground> {
  var _macbook = true;
  var _wifi = true;
  var _loadingMore = false;
  var _pageCount = 1;
  final _order = <String>['Design', 'Launch', 'Review'];

  static const _mailboxes = [
    ('Inbox', '128'),
    ('Sent', '42'),
    ('Drafts', '3'),
    ('Archive', '910'),
  ];

  @override
  Widget build(BuildContext context) {
    return ChicoColumn(
      gap: ChicoSpace.space16,
      children: [
        const _ListCaption(
          'ChicoGrid — adaptive columns (override with columns:)',
        ),
        ChicoGrid(
          children: [
            for (final entry in _mailboxes)
              ChicoCard(
                child: ChicoColumn(
                  gap: ChicoSpace.space4,
                  children: [
                    ChicoText(entry.$1, variant: ChicoTextVariant.headline),
                    ChicoText(
                      entry.$2,
                      role: ChicoTextRole.secondary,
                      variant: ChicoTextVariant.footnote,
                    ),
                  ],
                ),
              ),
          ],
        ),
        const _ListCaption('ChicoGrid — fixed 3 columns'),
        ChicoGrid(
          columns: 3,
          gap: ChicoSpace.space8,
          children: const [
            ChicoCard(child: ChicoText('A')),
            ChicoCard(child: ChicoText('B')),
            ChicoCard(child: ChicoText('C')),
          ],
        ),
        const _ListCaption('ChicoListTile — leading, trailing, chevron'),
        ChicoCard(
          padding: EdgeInsets.zero,
          child: Column(
            children: [
              ChicoListTile(
                title: 'Ada Lovelace',
                subtitle: 'Mathematician',
                leading: ChicoAvatar.name('Ada Lovelace'),
                onPressed: () {},
              ),
              const ChicoDivider(),
              ChicoListTile(
                title: 'Wi‑Fi',
                trailing: ChicoSwitch(
                  value: _wifi,
                  onChanged: (value) => setState(() => _wifi = value),
                ),
              ),
              const ChicoDivider(),
              ChicoListTile(
                title: 'Language',
                trailing: const ChicoText(
                  'English',
                  role: ChicoTextRole.secondary,
                ),
                showChevron: true,
                onPressed: () {},
              ),
              const ChicoDivider(),
              const ChicoListTile(title: 'Static row'),
            ],
          ),
        ),
        const _ListCaption('ChicoListSection — header, footer, grouped rows'),
        ChicoListSection(
          header: 'Devices',
          footer: 'Swipe MacBook toward the start to delete.',
          children: [
            const ChicoListTile(title: 'iPhone', subtitle: 'This iPhone'),
            if (_macbook)
              ChicoSwipe(
                actions: [
                  ChicoSwipeAction(
                    label: 'Delete',
                    onPressed: () => setState(() => _macbook = false),
                  ),
                ],
                child: const ChicoListTile(
                  title: 'MacBook Air',
                  subtitle: 'Online',
                ),
              ),
            ChicoListTile(
              title: 'iCloud',
              trailing: const ChicoText('On', role: ChicoTextRole.secondary),
              onPressed: () {},
            ),
          ],
        ),
        if (!_macbook)
          ChicoButton(
            label: 'Restore MacBook row',
            expand: true,
            tone: ChicoButtonTone.neutral,
            onPressed: () => setState(() => _macbook = true),
          ),
        const _ListCaption('ChicoCarousel — horizontal pager + dots'),
        ChicoCarousel(
          height: 120,
          onChanged: (_) {},
          children: [
            for (final label in ['One', 'Two', 'Three'])
              ChicoCard(
                child: Center(
                  child: ChicoText(label, variant: ChicoTextVariant.title),
                ),
              ),
          ],
        ),
        const _ListCaption('ChicoReorderableList — long-press to drag'),
        ChicoReorderableList(
          onReorder: (oldIndex, newIndex) {
            setState(() {
              final item = _order.removeAt(oldIndex);
              _order.insert(newIndex, item);
            });
          },
          children: [
            for (final name in _order)
              ChicoListTile(key: ValueKey(name), title: name),
          ],
        ),
        const _ListCaption('ChicoStickyList — pinned section headers'),
        SizedBox(
          height: 200,
          child: ChicoStickyList(
            groups: [
              ChicoStickyGroup(
                header: 'A',
                children: const [
                  ChicoListTile(title: 'Ada'),
                  ChicoListTile(title: 'Alan'),
                  ChicoListTile(title: 'Alice'),
                ],
              ),
              ChicoStickyGroup(
                header: 'B',
                children: const [
                  ChicoListTile(title: 'Barbara'),
                  ChicoListTile(title: 'Bob'),
                ],
              ),
              ChicoStickyGroup(
                header: 'C',
                children: const [ChicoListTile(title: 'Carol')],
              ),
            ],
          ),
        ),
        const _ListCaption('ChicoIndexedList — sticky groups + A–Z rail'),
        const SizedBox(
          height: 220,
          child: ChicoIndexedList(
            groups: [
              ChicoStickyGroup(
                header: 'A',
                children: [
                  ChicoListTile(title: 'Ada'),
                  ChicoListTile(title: 'Alan'),
                ],
              ),
              ChicoStickyGroup(
                header: 'G',
                children: [ChicoListTile(title: 'Grace')],
              ),
              ChicoStickyGroup(
                header: 'L',
                children: [
                  ChicoListTile(title: 'Linus'),
                  ChicoListTile(title: 'Lisa'),
                ],
              ),
              ChicoStickyGroup(
                header: 'T',
                children: [ChicoListTile(title: 'Tim')],
              ),
            ],
          ),
        ),
        const _ListCaption('ChicoLoadMore — paged list footer'),
        ChicoColumn(
          gap: ChicoSpace.space8,
          children: [
            for (var i = 1; i <= _pageCount * 2; i++)
              ChicoListTile(title: 'Row $i'),
            ChicoLoadMore(
              loading: _loadingMore,
              onPressed: _loadingMore
                  ? null
                  : () async {
                      setState(() => _loadingMore = true);
                      await Future<void>.delayed(
                        const Duration(milliseconds: 700),
                      );
                      if (!mounted) {
                        return;
                      }
                      setState(() {
                        _loadingMore = false;
                        _pageCount++;
                      });
                    },
            ),
          ],
        ),
      ],
    );
  }
}

class _ListCaption extends StatelessWidget {
  const _ListCaption(this.label);

  final String label;

  @override
  Widget build(BuildContext context) {
    return ChicoText(
      label,
      variant: ChicoTextVariant.footnote,
      role: ChicoTextRole.secondary,
    );
  }
}

class _ContentPlayground extends StatefulWidget {
  const _ContentPlayground();

  @override
  State<_ContentPlayground> createState() => _ContentPlaygroundState();
}

class _ContentPlaygroundState extends State<_ContentPlayground> {
  var _page = 0;
  var _empty = false;
  var _ticks = 0;
  var _step = 1;

  @override
  Widget build(BuildContext context) {
    return ChicoColumn(
      gap: ChicoSpace.space12,
      children: [
        if (_empty)
          ChicoEmptyState(
            icon: const ChicoGlyph(ChicoGlyphKind.search, size: 36),
            title: 'No Results',
            message: 'Try a different name or keyword.',
            actionLabel: 'Show list',
            onAction: () => setState(() => _empty = false),
          )
        else
          ChicoButton(
            label: 'Show empty state',
            expand: true,
            tone: ChicoButtonTone.neutral,
            onPressed: () => setState(() => _empty = true),
          ),
        ChicoDisclosure(
          title: 'Advanced',
          child: ChicoText(
            'VPN is required on public networks.',
            role: ChicoTextRole.secondary,
          ),
        ),
        ChicoStepIndicator(
          count: 3,
          index: _step,
          labels: const ['Account', 'Profile', 'Done'],
          onChanged: (index) => setState(() => _step = index),
        ),
        ChicoButton(
          label: _step < 2 ? 'Next step' : 'Reset steps',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () {
            setState(() => _step = _step < 2 ? _step + 1 : 0);
          },
        ),
        ChicoPageControl(
          count: 3,
          index: _page,
          onChanged: (index) => setState(() => _page = index),
        ),
        ChicoText(
          'Page ${_page + 1} of 3 · pull the list to refresh ($_ticks)',
          variant: ChicoTextVariant.footnote,
          role: ChicoTextRole.secondary,
        ),
        SizedBox(
          height: 160,
          child: ChicoRefresh(
            onRefresh: () async {
              await Future<void>.delayed(const Duration(milliseconds: 600));
              if (mounted) {
                setState(() => _ticks++);
              }
            },
            child: ListView(
              physics: const AlwaysScrollableScrollPhysics(
                parent: BouncingScrollPhysics(),
              ),
              children: [
                for (var i = 1; i <= 6; i++) ChicoListTile(title: 'Item $i'),
              ],
            ),
          ),
        ),
      ],
    );
  }
}

class _NavigationPlayground extends StatefulWidget {
  const _NavigationPlayground();

  @override
  State<_NavigationPlayground> createState() => _NavigationPlaygroundState();
}

class _NavigationPlaygroundState extends State<_NavigationPlayground> {
  var _tab = 1;
  var _nav = 0;

  static const _tabs = ['Day', 'Week', 'Month'];
  static const _navLabels = ['Home', 'Search', 'You'];

  @override
  Widget build(BuildContext context) {
    return ChicoCard(
      padding: EdgeInsets.zero,
      child: SizedBox(
        height: 280,
        child: ChicoScaffold(
          bar: ChicoBar(
            title: 'Inbox',
            safeArea: false,
            progress: 0.45,
            onBack: () {},
            trailing: ChicoBarButton(
              icon: const ChicoGlyph(ChicoGlyphKind.add),
              tooltip: 'Add',
              onPressed: () {},
            ),
          ),
          body: Padding(
            padding: const EdgeInsetsDirectional.all(ChicoSpace.space16),
            child: ChicoColumn(
              gap: ChicoSpace.space12,
              children: [
                ChicoTabView(
                  labels: _tabs,
                  index: _tab,
                  onChanged: (index) => setState(() => _tab = index),
                  children: [
                    for (final label in _tabs)
                      ChicoText(label, role: ChicoTextRole.secondary),
                  ],
                ),
                ChicoText(
                  _navLabels[_nav],
                  variant: ChicoTextVariant.footnote,
                  role: ChicoTextRole.secondary,
                ),
              ],
            ),
          ),
          bottomNav: ChicoBottomNav(
            index: _nav,
            safeArea: false,
            onChanged: (index) => setState(() => _nav = index),
            items: const [
              ChicoNavItem(
                label: 'Home',
                icon: ChicoGlyph(ChicoGlyphKind.home),
              ),
              ChicoNavItem(
                label: 'Search',
                icon: ChicoGlyph(ChicoGlyphKind.search),
                badgeCount: 3,
              ),
              ChicoNavItem(
                label: 'You',
                icon: ChicoGlyph(ChicoGlyphKind.person),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _NavigationPlusPlayground extends StatefulWidget {
  const _NavigationPlusPlayground();

  @override
  State<_NavigationPlusPlayground> createState() =>
      _NavigationPlusPlaygroundState();
}

class _NavigationPlusPlaygroundState extends State<_NavigationPlusPlayground> {
  var _rail = 0;

  @override
  Widget build(BuildContext context) {
    return ChicoColumn(
      gap: ChicoSpace.space12,
      children: [
        const ChicoText(
          'Rail for md+ side navigation. Large title fades the compact bar in as you scroll.',
          role: ChicoTextRole.secondary,
          variant: ChicoTextVariant.footnote,
        ),
        ChicoCard(
          padding: EdgeInsets.zero,
          child: SizedBox(
            height: 280,
            child: ChicoScaffold(
              rail: ChicoRail(
                extended: true,
                safeArea: false,
                index: _rail,
                onChanged: (index) => setState(() => _rail = index),
                items: const [
                  ChicoNavItem(
                    label: 'Home',
                    icon: ChicoGlyph(ChicoGlyphKind.home),
                  ),
                  ChicoNavItem(
                    label: 'Search',
                    icon: ChicoGlyph(ChicoGlyphKind.search),
                  ),
                  ChicoNavItem(
                    label: 'You',
                    icon: ChicoGlyph(ChicoGlyphKind.person),
                  ),
                ],
              ),
              body: ChicoLargeTitle(
                title: ['Home', 'Search', 'You'][_rail],
                automaticallyImplyLeading: false,
                safeArea: false,
                search: const ChicoSearch(hint: 'Filter'),
                child: Padding(
                  padding: const EdgeInsetsDirectional.symmetric(
                    horizontal: ChicoSpace.space16,
                  ),
                  child: ChicoColumn(
                    gap: ChicoSpace.space8,
                    children: [
                      for (var i = 1; i <= 8; i++)
                        ChicoListTile(
                          title: 'Item $i',
                          subtitle: 'Scroll to collapse the title',
                          onPressed: () {},
                        ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

class _OverlaysPlayground extends StatelessWidget {
  const _OverlaysPlayground();

  @override
  Widget build(BuildContext context) {
    return ChicoColumn(
      gap: ChicoSpace.space12,
      children: [
        const ChicoText(
          'Hover or long-press the add control. Open the menu from More. Help is a popover.',
          role: ChicoTextRole.secondary,
          variant: ChicoTextVariant.footnote,
        ),
        ChicoRow(
          gap: ChicoSpace.space8,
          wrap: true,
          children: [
            ChicoTooltip(
              message: 'Add an item',
              child: ChicoIconButton(
                icon: const ChicoGlyph(ChicoGlyphKind.add),
                tooltip: 'Add',
                onPressed: () {},
              ),
            ),
            Builder(
              builder: (context) {
                return ChicoButton(
                  label: 'More',
                  tone: ChicoButtonTone.neutral,
                  onPressed: () {
                    ChicoMenu.show<String>(
                      context,
                      actions: const [
                        ChicoMenuAction(
                          label: 'Edit',
                          result: 'edit',
                          icon: ChicoGlyph(ChicoGlyphKind.check),
                        ),
                        ChicoMenuAction(label: 'Share', result: 'share'),
                        ChicoMenuAction(
                          label: 'Delete',
                          result: 'delete',
                          tone: ChicoButtonTone.destructive,
                        ),
                      ],
                    );
                  },
                );
              },
            ),
            Builder(
              builder: (context) {
                return ChicoButton(
                  label: 'Help',
                  tone: ChicoButtonTone.neutral,
                  onPressed: () {
                    ChicoPopover.show<void>(
                      context,
                      child: const Padding(
                        padding: EdgeInsets.all(ChicoSpace.space16),
                        child: SizedBox(
                          width: 220,
                          child: ChicoText(
                            'A popover holds any content. Tap the dimmed area to dismiss.',
                            role: ChicoTextRole.secondary,
                          ),
                        ),
                      ),
                    );
                  },
                );
              },
            ),
          ],
        ),
      ],
    );
  }
}

class _SheetsPlayground extends StatelessWidget {
  const _SheetsPlayground();

  @override
  Widget build(BuildContext context) {
    return ChicoColumn(
      gap: ChicoSpace.space12,
      children: [
        const ChicoText(
          'Sheet for custom content. Action sheet for a choice plus cancel. Toast is a non-blocking status pill.',
          role: ChicoTextRole.secondary,
          variant: ChicoTextVariant.footnote,
        ),
        ChicoButton(
          label: 'Show sheet',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () {
            ChicoSheet.show<void>(
              context,
              title: 'Filters',
              child: ChicoColumn(
                gap: ChicoSpace.space12,
                children: [
                  const ChicoText(
                    'Narrow the list. Dismiss from Done or the dimmed area.',
                    role: ChicoTextRole.secondary,
                  ),
                  ChicoButton(
                    label: 'Done',
                    expand: true,
                    onPressed: () => Navigator.of(context).pop(),
                  ),
                ],
              ),
            );
          },
        ),
        ChicoButton(
          label: 'Show half sheet',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () {
            ChicoSheet.show<void>(
              context,
              title: 'Detents',
              detent: ChicoSheetDetent.medium,
              child: const ChicoText(
                'Drag the grabber up for full, down to dismiss.',
                role: ChicoTextRole.secondary,
              ),
            );
          },
        ),
        ChicoButton(
          label: 'Show action sheet',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () {
            ChicoActionSheet.show<String>(
              context,
              title: 'Change photo',
              message: 'This replaces the current image.',
              actions: const [
                ChicoDialogAction(label: 'Take photo', result: 'camera'),
                ChicoDialogAction(
                  label: 'Choose from library',
                  result: 'library',
                ),
                ChicoDialogAction(
                  label: 'Delete',
                  result: 'delete',
                  tone: ChicoButtonTone.destructive,
                ),
              ],
              cancel: const ChicoDialogAction(
                label: 'Cancel',
                tone: ChicoButtonTone.neutral,
              ),
            );
          },
        ),
        ChicoButton(
          label: 'Show toast',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () {
            ChicoToast.show(context, message: 'Saved');
          },
        ),
      ],
    );
  }
}

class _PickersPlayground extends StatefulWidget {
  const _PickersPlayground();

  @override
  State<_PickersPlayground> createState() => _PickersPlaygroundState();
}

class _PickersPlaygroundState extends State<_PickersPlayground> {
  DateTime _date = DateTime(2024, 6, 15);
  ChicoTime _time = const ChicoTime(hour: 9, minute: 30);

  @override
  Widget build(BuildContext context) {
    final use24 = MediaQuery.alwaysUse24HourFormatOf(context);
    return ChicoColumn(
      gap: ChicoSpace.space12,
      children: [
        ChicoText(
          '${_date.year}-${_date.month.toString().padLeft(2, '0')}-${_date.day.toString().padLeft(2, '0')}  ·  ${_time.format(use24Hour: use24)}',
          role: ChicoTextRole.secondary,
        ),
        ChicoButton(
          label: 'Show date picker',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () async {
            final next = await ChicoDatePicker.show(
              context,
              initialDate: _date,
            );
            if (next != null) {
              setState(() => _date = next);
            }
          },
        ),
        ChicoButton(
          label: 'Show time picker',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () async {
            final next = await ChicoTimePicker.show(
              context,
              initialTime: _time,
            );
            if (next != null) {
              setState(() => _time = next);
            }
          },
        ),
        ChicoButton(
          label: 'Show date and time',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () async {
            final next = await ChicoDatePicker.showDateTime(
              context,
              initialDateTime: DateTime(
                _date.year,
                _date.month,
                _date.day,
                _time.hour,
                _time.minute,
              ),
            );
            if (next != null) {
              setState(() {
                _date = next;
                _time = ChicoTime.fromDateTime(next);
              });
            }
          },
        ),
        ChicoButton(
          label: 'Show date range',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () {
            ChicoDatePicker.showRange(context);
          },
        ),
      ],
    );
  }
}

class _SelectPlayground extends StatefulWidget {
  const _SelectPlayground();

  @override
  State<_SelectPlayground> createState() => _SelectPlaygroundState();
}

class _SelectPlaygroundState extends State<_SelectPlayground> {
  String? _role = 'member';

  @override
  Widget build(BuildContext context) {
    return ChicoSelect<String>(
      label: 'Role',
      value: _role,
      options: const [
        ChicoSelectOption(value: 'admin', label: 'Admin'),
        ChicoSelectOption(value: 'member', label: 'Member'),
        ChicoSelectOption(value: 'guest', label: 'Guest'),
      ],
      onChanged: (value) => setState(() => _role = value),
    );
  }
}

class _AdaptivePlayground extends StatefulWidget {
  const _AdaptivePlayground();

  @override
  State<_AdaptivePlayground> createState() => _AdaptivePlaygroundState();
}

class _AdaptivePlaygroundState extends State<_AdaptivePlayground> {
  var _tab = 0;
  String? _thread;

  static const _items = [
    ChicoNavItem(label: 'Home', icon: ChicoGlyph(ChicoGlyphKind.home)),
    ChicoNavItem(label: 'Mail', icon: ChicoGlyph(ChicoGlyphKind.mail)),
    ChicoNavItem(label: 'You', icon: ChicoGlyph(ChicoGlyphKind.person)),
  ];

  @override
  Widget build(BuildContext context) {
    return ChicoCard(
      padding: EdgeInsets.zero,
      child: SizedBox(
        height: 280,
        child: ChicoAdaptiveScaffold(
          index: _tab,
          onDestinationSelected: (index) => setState(() => _tab = index),
          destinations: _items,
          bar: ChicoBar(
            title: _items[_tab].label,
            automaticallyImplyLeading: false,
            safeArea: false,
            bottom: const ChicoSearch(hint: 'Filter'),
          ),
          body: ChicoSplitView(
            list: ListView(
              padding: const EdgeInsetsDirectional.all(ChicoSpace.space8),
              children: [
                for (final name in ['Design', 'Launch', 'Review'])
                  ChicoListTile(
                    title: name,
                    onPressed: () => setState(() => _thread = name),
                  ),
              ],
            ),
            detail: ChicoPad(
              all: ChicoSpace.space16,
              child: ChicoText(
                _thread == null ? 'Pick a thread' : _thread!,
                variant: ChicoTextVariant.title,
              ),
            ),
            showDetailOnCompact: _thread != null,
          ),
        ),
      ),
    );
  }
}

class _DataPlayground extends StatefulWidget {
  const _DataPlayground();

  @override
  State<_DataPlayground> createState() => _DataPlaygroundState();
}

class _DataPlaygroundState extends State<_DataPlayground> {
  @override
  Widget build(BuildContext context) {
    return ChicoColumn(
      gap: ChicoSpace.space12,
      children: [
        ChicoBreadcrumbs(
          items: [
            ChicoBreadcrumb(label: 'Inbox', onPressed: () {}),
            const ChicoBreadcrumb(label: 'Design'),
          ],
        ),
        ChicoContextMenu(
          actions: const [
            ChicoMenuAction(label: 'Star', result: 'star'),
            ChicoMenuAction(
              label: 'Delete',
              result: 'delete',
              tone: ChicoButtonTone.destructive,
            ),
          ],
          child: const ChicoListTile(
            title: 'Long-press or right-click',
            subtitle: 'Opens a context menu',
          ),
        ),
        const ChicoTable(
          columns: [
            ChicoTableColumn(label: 'Mailbox'),
            ChicoTableColumn(label: 'Count', numeric: true, flex: 1),
          ],
          rows: [
            ['Inbox', '128'],
            ['Sent', '42'],
          ],
        ),
        const SizedBox(
          height: 180,
          child: ChicoStatusPage.empty(
            title: 'No results',
            message: 'Try a different filter.',
          ),
        ),
        ChicoButton(
          label: 'Show shortcuts',
          expand: true,
          tone: ChicoButtonTone.neutral,
          onPressed: () {
            ChicoShortcuts.show(
              context,
              items: const [
                ChicoShortcutItem(keys: '⌘K', label: 'Search'),
                ChicoShortcutItem(keys: '⌘N', label: 'New'),
              ],
            );
          },
        ),
      ],
    );
  }
}

class _WindowCard extends StatelessWidget {
  const _WindowCard({required this.media, required this.theme});

  final ChicoMediaData media;
  final ChicoThemeData theme;

  @override
  Widget build(BuildContext context) {
    return DecoratedBox(
      decoration: BoxDecoration(
        color: theme.colors.groupedBackground,
        borderRadius: ChicoRadius.borderLg,
        border: Border.all(
          color: theme.colors.separator,
          width: media.hairline,
        ),
      ),
      child: Padding(
        padding: EdgeInsets.all(media.space(ChicoSpace.space16)),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            ChicoText(
              '${media.width.round()} × ${media.height.round()}  ·  '
              '${media.breakpoint.name}  ·  ${media.isRtl ? 'RTL' : 'LTR'}',
              variant: ChicoTextVariant.headline,
            ),
            SizedBox(height: media.space(ChicoSpace.space4)),
            ChicoText(
              'paddingScale ${media.paddingScale}  ·  '
              'displayScale ${media.displayScale}  ·  '
              '${media.isCompact ? 'compact' : 'regular'}',
              role: ChicoTextRole.secondary,
              variant: ChicoTextVariant.footnote,
            ),
          ],
        ),
      ),
    );
  }
}

class _ColorGrid extends StatelessWidget {
  const _ColorGrid({required this.theme, required this.media});

  final ChicoThemeData theme;
  final ChicoMediaData media;

  @override
  Widget build(BuildContext context) {
    final colors = theme.colors;
    final swatches = <(String, Color)>[
      ('label', colors.label),
      ('secondary', colors.secondaryLabel),
      ('tertiary', colors.tertiaryLabel),
      ('background', colors.background),
      ('grouped', colors.groupedBackground),
      ('fill', colors.fill),
      ('separator', colors.separator),
      ('tint', colors.tint),
      ('destructive', colors.destructive),
      ('success', colors.success),
      ('warning', colors.warning),
    ];

    return Wrap(
      spacing: media.space(ChicoSpace.space12),
      runSpacing: media.space(ChicoSpace.space12),
      children: [
        for (final swatch in swatches)
          SizedBox(
            width: 96,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                DecoratedBox(
                  decoration: BoxDecoration(
                    color: swatch.$2,
                    borderRadius: ChicoRadius.borderSm,
                    border: Border.all(
                      color: theme.colors.separator,
                      width: media.hairline,
                    ),
                  ),
                  child: const SizedBox(width: 96, height: 36),
                ),
                SizedBox(height: media.space(ChicoSpace.space4)),
                ChicoText(
                  swatch.$1,
                  variant: ChicoTextVariant.caption,
                  role: ChicoTextRole.secondary,
                ),
              ],
            ),
          ),
      ],
    );
  }
}

class _SpacingScale extends StatelessWidget {
  const _SpacingScale({required this.theme, required this.media});

  final ChicoThemeData theme;
  final ChicoMediaData media;

  static const _tokens = <(String, double)>[
    ('2', ChicoSpace.space2),
    ('4', ChicoSpace.space4),
    ('8', ChicoSpace.space8),
    ('12', ChicoSpace.space12),
    ('16', ChicoSpace.space16),
    ('20', ChicoSpace.space20),
    ('24', ChicoSpace.space24),
    ('32', ChicoSpace.space32),
    ('40', ChicoSpace.space40),
    ('48', ChicoSpace.space48),
  ];

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        for (final token in _tokens)
          Padding(
            padding: EdgeInsets.only(bottom: media.space(ChicoSpace.space8)),
            child: Row(
              children: [
                SizedBox(
                  width: 36,
                  child: ChicoText(
                    token.$1,
                    variant: ChicoTextVariant.caption,
                    role: ChicoTextRole.secondary,
                  ),
                ),
                DecoratedBox(
                  decoration: BoxDecoration(
                    color: theme.colors.tint,
                    borderRadius: ChicoRadius.borderSm,
                  ),
                  child: SizedBox(height: 8, width: token.$2),
                ),
              ],
            ),
          ),
      ],
    );
  }
}

class _RoadmapItem {
  const _RoadmapItem({
    required this.category,
    required this.widgets,
    required this.ready,
  });

  final String category;
  final String widgets;
  final bool ready;
}

const _roadmap = <_RoadmapItem>[
  _RoadmapItem(
    category: 'Foundation',
    widgets: 'tokens, theme, media, ChicoApp',
    ready: true,
  ),
  _RoadmapItem(category: 'Typography', widgets: 'ChicoText', ready: true),
  _RoadmapItem(
    category: 'Layout',
    widgets: 'Gap, Pad, Row, Column, Page, Grid',
    ready: true,
  ),
  _RoadmapItem(
    category: 'Actions',
    widgets: 'Button, IconButton, Link',
    ready: true,
  ),
  _RoadmapItem(
    category: 'Forms',
    widgets:
        'TextField, Form, Select, Autocomplete, Pin, Number, Token, Checkbox, Switch, Radio, Slider, Range, Rating',
    ready: true,
  ),
  _RoadmapItem(
    category: 'Feedback',
    widgets:
        'Spinner, Progress, Banner, Dialog, Busy, Skeleton, LoadMore, StatusPage',
    ready: true,
  ),
  _RoadmapItem(
    category: 'Surfaces',
    widgets:
        'Divider, Card, ListTile, Avatar, AvatarStack, Badge, Table, Image, Reorderable, Sticky, Indexed',
    ready: true,
  ),
  _RoadmapItem(category: 'Lists+', widgets: 'ListSection, Swipe', ready: true),
  _RoadmapItem(
    category: 'Content+',
    widgets: 'EmptyState, Disclosure, PageControl, Refresh, StepIndicator',
    ready: true,
  ),
  _RoadmapItem(
    category: 'Navigation',
    widgets:
        'Bar, Tabs, TabView, BottomNav, AdaptiveScaffold, SplitView, Breadcrumbs, PageRoute',
    ready: true,
  ),
  _RoadmapItem(
    category: 'Overlays',
    widgets: 'Tooltip, Menu, Popover, ContextMenu, Shortcuts',
    ready: true,
  ),
  _RoadmapItem(
    category: 'Sheets',
    widgets: 'Sheet, ActionSheet, Toast, detents',
    ready: true,
  ),
  _RoadmapItem(
    category: 'Inputs+',
    widgets: 'Search, Chip, Stepper, TokenField',
    ready: true,
  ),
  _RoadmapItem(
    category: 'Pickers',
    widgets: 'Date, Time, DateRange, DateTime',
    ready: true,
  ),
  _RoadmapItem(
    category: 'Navigation+',
    widgets: 'Rail, large title, carousel, bar search',
    ready: true,
  ),
];

class _RoadmapRow extends StatelessWidget {
  const _RoadmapRow({
    required this.item,
    required this.theme,
    required this.media,
  });

  final _RoadmapItem item;
  final ChicoThemeData theme;
  final ChicoMediaData media;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.only(bottom: media.space(ChicoSpace.space12)),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsetsDirectional.only(
              top: 6,
              end: ChicoSpace.space12,
            ),
            child: DecoratedBox(
              decoration: BoxDecoration(
                color: item.ready ? theme.colors.tint : theme.colors.separator,
                shape: BoxShape.circle,
              ),
              child: const SizedBox(width: 8, height: 8),
            ),
          ),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                ChicoText(item.category, variant: ChicoTextVariant.callout),
                ChicoText(
                  item.widgets,
                  variant: ChicoTextVariant.footnote,
                  role: ChicoTextRole.secondary,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
0
likes
160
points
93
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Zero-dependency Flutter UI kit for minimal system interfaces. Responsive, adaptive, RTL-first widgets built from design tokens.

Repository (GitHub)
View/report issues

Topics

#flutter #ui #widget #design-system #rtl

License

MIT (license)

Dependencies

flutter

More

Packages that depend on chico_ui