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/flutterThen run:
flutter pub getAlternative: 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 --flutterSee 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:
- Add
cookest_uias a dependency (see Installation above) - Replace
AppTheme.lightThemewithCookestTheme.light - Replace
AppTheme.darkThemewithCookestTheme.dark - Replace custom widgets with
Ck*equivalents - Remove
shadcn_theme.dart