Cookest
@cookest/ui Components

Flutter — Getting Started

How to install and use the cookest_ui Flutter package in your app.

Flutter — Getting Started

cookest_ui is a Flutter widget library providing the same design system and components as the web @cookest/ui React package. All widgets use the shared design tokens generated by Style Dictionary.

Installation

Add the package as a path dependency in your pubspec.yaml:

dependencies:
  cookest_ui:
    path: ../ui-components/flutter

Then run:

flutter pub get

Alternative: CLI install

Copy individual widget source files directly into your project:

npx @cookest/ui-cli add button --flutter
npx @cookest/ui-cli add tabs accordion --flutter

See the CLI docs for full reference.

Setup

Apply the Cookest theme in your MaterialApp:

import 'package:cookest_ui/cookest_ui.dart';

MaterialApp(
  theme: CookestTheme.light,
  darkTheme: CookestTheme.dark,
  themeMode: ThemeMode.system,
  home: const MyHomePage(),
);

This configures Material 3 with the Cookest color scheme, typography (Playfair Display + Inter), and component styles.

Widgets

CkButton

CkButton(
  onPressed: () {},
  variant: CkButtonVariant.primary,  // primary, secondary, ghost, danger
  size: CkButtonSize.md,             // sm, md, lg
  loading: false,
  fullWidth: false,
  iconLeft: Icon(Icons.download, size: 16),
  child: Text('Download'),
)

CkInput

CkInput(
  label: 'Email',
  placeholder: 'you@example.com',
  helperText: 'We will never share your email',
  error: 'Invalid email',
  inputSize: CkInputSize.md,  // sm, md, lg
  iconLeft: Icon(Icons.email, size: 20),
  onChanged: (value) => print(value),
)

CkCard

CkCard(
  variant: CkCardVariant.interactive,
  padding: CkCardPadding.md,
  onTap: () {},
  child: Column(
    children: [
      CkCardHeader(child: Text('Recipe')),
      CkCardBody(child: Text('A delicious meal')),
      CkCardFooter(
        child: CkButton(
          onPressed: () {},
          size: CkButtonSize.sm,
          child: Text('Cook'),
        ),
      ),
    ],
  ),
)

CkBadge

CkBadge(
  variant: CkBadgeVariant.success,
  dot: true,
  child: Text('Active'),
)

CkBadge(
  removable: true,
  onRemove: () {},
  child: Text('Gluten-free'),
)

CkAvatar

// With image
CkAvatar(
  imageUrl: 'https://example.com/photo.jpg',
  alt: 'John Doe',
  size: CkAvatarSize.lg,
)

// With initials fallback
CkAvatar(alt: 'John Doe', size: CkAvatarSize.md)

// Group with overflow
CkAvatarGroup(
  max: 3,
  children: [
    CkAvatar(alt: 'Alice'),
    CkAvatar(alt: 'Bob'),
    CkAvatar(alt: 'Charlie'),
    CkAvatar(alt: 'Diana'),
  ],
)

CkModal

// Show a modal dialog
CkModal.show(
  context: context,
  title: 'Confirm Action',
  size: CkModalSize.md,
  builder: (ctx) => Text('Are you sure you want to delete this recipe?'),
  footer: Row(
    mainAxisAlignment: MainAxisAlignment.end,
    children: [
      CkButton(
        variant: CkButtonVariant.ghost,
        onPressed: () => Navigator.pop(context),
        child: Text('Cancel'),
      ),
      SizedBox(width: 12),
      CkButton(
        variant: CkButtonVariant.danger,
        onPressed: () { /* delete */ },
        child: Text('Delete'),
      ),
    ],
  ),
);

CkToggle

CkToggle(
  value: isDarkMode,
  onChanged: (v) => setState(() => isDarkMode = v),
  label: 'Dark Mode',
  description: 'Enable dark color scheme',
  toggleSize: CkToggleSize.md,
)

CkSelect

CkSelect(
  label: 'Cuisine',
  options: [
    CkSelectOption(value: 'italian', label: 'Italian'),
    CkSelectOption(value: 'japanese', label: 'Japanese'),
    CkSelectOption(value: 'mexican', label: 'Mexican'),
  ],
  value: selectedCuisine,
  onChanged: (v) => setState(() => selectedCuisine = v),
  searchable: true,
  placeholder: 'Choose a cuisine...',
)

CkAlert

The Alert uses a translucent colored background with a left accent bar.

CkAlert(
  variant: CkAlertVariant.warning,
  title: 'Low Stock',
  dismissible: true,
  onDismiss: () {},
  child: Text('Some ingredients in your pantry are running low.'),
)

CkSlider

CkSlider(
  value: _cookTime,
  min: 5,
  max: 120,
  step: 5,
  color: CkSliderColor.primary,   // primary, blue, amber, rose
  size: CkSliderSize.md,          // sm, md, lg
  showValue: true,
  label: 'Cook Time (min)',
  onChanged: (v) => setState(() => _cookTime = v),
  marks: const [
    CkSliderMark(value: 5, label: '5m'),
    CkSliderMark(value: 60, label: '1h'),
    CkSliderMark(value: 120, label: '2h'),
  ],
)

CkProgress

// Determinate (0–100)
CkProgress(
  value: _uploadProgress,
  color: CkProgressColor.primary,
  showLabel: true,
  label: 'Uploading recipe...',
)

// Indeterminate (null value)
CkProgress(color: CkProgressColor.blue)

// Striped
CkProgress(value: 60, striped: true, animated: true)

CkSpinner

// Standalone
const CkSpinner(
  color: CkSpinnerColor.primary,  // primary, blue, amber, rose, white, muted
  size: CkSpinnerSize.md,         // sm, md, lg, xl
)

// Inside a button
CkButton(
  onPressed: _isSaving ? null : _save,
  child: _isSaving
      ? const Row(children: [
          CkSpinner(color: CkSpinnerColor.white, size: CkSpinnerSize.sm),
          SizedBox(width: 8),
          Text('Saving...'),
        ])
      : const Text('Save'),
)

CkTabs

CkTabs(
  variant: CkTabsVariant.underline,  // underline, pills, boxed
  items: [
    CkTabItem(
      id: 'ingredients',
      label: 'Ingredients',
      icon: const Icon(Icons.list_alt),
      content: IngredientsList(),
    ),
    CkTabItem(
      id: 'method',
      label: 'Method',
      badge: '5 steps',
      content: MethodSteps(),
    ),
    CkTabItem(
      id: 'nutrition',
      label: 'Nutrition',
      content: NutritionPanel(),
    ),
  ],
  onChanged: (id) => debugPrint('Switched to $id'),
)

CkAccordion

CkAccordion(
  variant: CkAccordionVariant.default_,  // default_, bordered, separated
  multiple: false,
  defaultOpen: {'q1'},
  items: [
    CkAccordionItem(
      id: 'q1',
      title: 'Ingredient Substitutions',
      subtitle: '3 alternatives',
      content: Text('You can replace butter with coconut oil...'),
    ),
    CkAccordionItem(
      id: 'q2',
      title: 'Storage & Reheating',
      content: Text('Store in an airtight container for up to 3 days.'),
    ),
  ],
)

CkTextarea

CkTextarea(
  label: 'Recipe Description',
  placeholder: 'Describe the flavors, technique, and origin...',
  maxLength: 500,
  showCount: true,
  minLines: 4,
  maxLines: 8,
  inputSize: CkTextareaSize.md,  // sm, md, lg
  onChanged: (val) => setState(() => _description = val),
)

// With validation
CkTextarea(
  label: 'Notes',
  errorText: _error,
  helperText: 'Optional cooking notes',
)

CkSkeleton

// Single line
CkSkeleton(variant: CkSkeletonVariant.text)

// Multi-line
CkSkeleton(variant: CkSkeletonVariant.text, lines: 3)

// Circular (avatar placeholder)
CkSkeleton(variant: CkSkeletonVariant.circular, width: 48, height: 48)

// Full card placeholder
CkSkeletonCard()

CkDivider

// Simple horizontal
CkDivider()

// With label
CkDivider(label: 'OR')

// Vertical (inside a Row)
Row(
  children: [
    Text('Left'),
    SizedBox(height: 40, child: CkDivider(orientation: CkDividerOrientation.vertical)),
    Text('Right'),
  ],
)

CkTooltip

CkTooltip(
  message: 'Save your recipe',
  position: CkTooltipPosition.top,
  child: CkButton(
    onPressed: () {},
    child: Text('Save'),
  ),
)

Using Tokens Directly

Access the generated design tokens for custom widgets:

import 'package:cookest_ui/cookest_ui.dart';

Container(
  padding: EdgeInsets.all(CookestTokens.spacingN4),
  decoration: BoxDecoration(
    color: CookestTokens.colorSurfaceLight,
    borderRadius: BorderRadius.circular(CookestTokens.radiusXl),
    border: Border.all(color: CookestTokens.colorBorderLight),
  ),
  child: Text(
    'Custom widget using Cookest tokens',
    style: TextStyle(
      fontFamily: CookestTokens.fontFamilySans,
      fontSize: CookestTokens.fontSizeSm,
      color: CookestTokens.colorTextLight,
    ),
  ),
)

Migrating from the Existing App Theme

The cookest_ui package replaces the existing shadcn_theme.dart in the Flutter app. To migrate:

  1. Add cookest_ui as a dependency (see Installation above)
  2. Replace AppTheme.lightTheme with CookestTheme.light
  3. Replace AppTheme.darkTheme with CookestTheme.dark
  4. Replace custom widgets with Ck* equivalents
  5. Remove shadcn_theme.dart

On this page