garnish_ui 0.3.2
garnish_ui: ^0.3.2 copied to clipboard
A premium, brand-agnostic Flutter UI component library with built-in animations, 5 visual style categories, fully customizable theming, and 40+ accessible components.
example/lib/main.dart
// Copyright 2026 Garnish UI. All rights reserved.
// Use of this source code is governed by an MIT license that can be
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
import 'package:garnish_ui/garnish_ui.dart';
void main() {
runApp(const GarnishUIExample());
}
/// The root widget for the Garnish UI example app.
class GarnishUIExample extends StatelessWidget {
/// Creates the Garnish UI example app.
const GarnishUIExample({super.key});
@override
Widget build(BuildContext context) {
// Wrap your app with GarnishApp to provide theming
return GarnishApp(
theme: GarnishTheme.light(),
darkTheme: GarnishTheme.dark(),
themeMode: ThemeMode.system,
child: WidgetsApp(
title: 'Garnish UI Example',
color: const Color(0xFF6366F1),
home: const ExampleHomePage(),
),
);
}
}
/// The main example page showcasing Garnish UI components.
class ExampleHomePage extends StatefulWidget {
/// Creates the example home page.
const ExampleHomePage({super.key});
@override
State<ExampleHomePage> createState() => _ExampleHomePageState();
}
class _ExampleHomePageState extends State<ExampleHomePage> {
// Animation controller for programmatic animations
final _shakeController = GarnishAnimationController();
// Form state
bool _checkboxValue = false;
bool _switchValue = false;
String? _selectedOption;
// Style category for the styled container demo
GarnishStyleCategory _styleCategory = GarnishStyleCategory.minimal;
@override
Widget build(BuildContext context) {
final theme = GarnishTheme.of(context);
return SingleChildScrollView(
padding: EdgeInsets.all(theme.spacing.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
Text(
'Garnish UI Demo',
style: theme.typography.headlineLarge.copyWith(
color: theme.colors.onBackground,
),
),
SizedBox(height: theme.spacing.md),
Text(
'A premium, brand-agnostic Flutter UI component library with built-in animations.',
style: theme.typography.bodyLarge.copyWith(
color: theme.colors.onBackground,
),
),
SizedBox(height: theme.spacing.xxl),
// ============================================================
// ANIMATION SYSTEM DEMO
// ============================================================
_SectionTitle(title: 'Animation System'),
SizedBox(height: theme.spacing.md),
// Animated buttons with different presets
Wrap(
spacing: theme.spacing.sm,
runSpacing: theme.spacing.sm,
children: [
// Fade slide up animation
GarnishButton(
animation: GarnishAnimation.fadeSlideUp(),
onPressed: () {},
child: const Text('Fade Slide Up'),
),
// Scale animation
GarnishButton(
animation: GarnishAnimation.scale(),
onPressed: () {},
variant: GarnishButtonVariant.secondary,
child: const Text('Scale'),
),
// Bounce animation
GarnishButton(
animation: GarnishAnimation.bounce(),
onPressed: () {},
variant: GarnishButtonVariant.outlined,
child: const Text('Bounce'),
),
],
),
SizedBox(height: theme.spacing.md),
// Programmatic shake animation
Row(
children: [
Expanded(
child: GarnishTextField(
animation: GarnishAnimation.shake(animateOnMount: false),
animationController: _shakeController,
placeholder: 'Enter something...',
onChanged: (_) {},
),
),
SizedBox(width: theme.spacing.sm),
GarnishButton(
onPressed: () => _shakeController.play(),
variant: GarnishButtonVariant.destructive,
child: const Text('Trigger Shake'),
),
],
),
SizedBox(height: theme.spacing.xxl),
// ============================================================
// STYLE CATEGORIES DEMO
// ============================================================
_SectionTitle(title: 'Style Categories'),
SizedBox(height: theme.spacing.md),
Text(
'Apply different visual styles to components:',
style: theme.typography.bodyMedium.copyWith(
color: theme.colors.onBackground,
),
),
SizedBox(height: theme.spacing.md),
// Style selector
Wrap(
spacing: theme.spacing.xs,
runSpacing: theme.spacing.xs,
children: GarnishStyleCategory.values.map((category) {
final isSelected = category == _styleCategory;
return GarnishButton(
onPressed: () => setState(() => _styleCategory = category),
variant: isSelected
? GarnishButtonVariant.primary
: GarnishButtonVariant.text,
size: GarnishButtonSize.small,
child: Text(_styleCategoryName(category)),
);
}).toList(),
),
SizedBox(height: theme.spacing.md),
// Styled container demo
GarnishStyledContainer(
styleCategory: _styleCategory,
padding: EdgeInsets.all(theme.spacing.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_styleCategoryName(_styleCategory),
style: theme.typography.titleLarge.copyWith(
color: theme.colors.onSurface,
),
),
SizedBox(height: theme.spacing.sm),
Text(
_styleCategoryDescription(_styleCategory),
style: theme.typography.bodyMedium.copyWith(
color: theme.colors.onSurface,
),
),
],
),
),
SizedBox(height: theme.spacing.xxl),
// ============================================================
// BUTTON VARIANTS
// ============================================================
_SectionTitle(title: 'Button Variants'),
SizedBox(height: theme.spacing.md),
Wrap(
spacing: theme.spacing.sm,
runSpacing: theme.spacing.sm,
children: [
GarnishButton(
onPressed: () {},
variant: GarnishButtonVariant.primary,
child: const Text('Primary'),
),
GarnishButton(
onPressed: () {},
variant: GarnishButtonVariant.secondary,
child: const Text('Secondary'),
),
GarnishButton(
onPressed: () {},
variant: GarnishButtonVariant.outlined,
child: const Text('Outlined'),
),
GarnishButton(
onPressed: () {},
variant: GarnishButtonVariant.text,
child: const Text('Text'),
),
GarnishButton(
onPressed: () {},
variant: GarnishButtonVariant.destructive,
child: const Text('Destructive'),
),
GarnishButton(
onPressed: null, // Disabled
child: const Text('Disabled'),
),
GarnishButton(
onPressed: () {},
isLoading: true,
child: const Text('Loading'),
),
],
),
SizedBox(height: theme.spacing.xxl),
// ============================================================
// INPUT COMPONENTS
// ============================================================
_SectionTitle(title: 'Input Components'),
SizedBox(height: theme.spacing.md),
// Text field with various states
GarnishTextField(
label: 'Username',
placeholder: 'Enter your username',
onChanged: (_) {},
helperText: 'This is a helper text',
),
SizedBox(height: theme.spacing.md),
GarnishTextField(
label: 'Email',
placeholder: 'Enter your email',
onChanged: (_) {},
errorText: 'Invalid email address',
),
SizedBox(height: theme.spacing.md),
// Checkbox
GarnishCheckbox(
value: _checkboxValue,
onChanged: (value) => setState(() => _checkboxValue = value ?? false),
label: 'Accept terms and conditions',
),
SizedBox(height: theme.spacing.sm),
// Switch
GarnishSwitch(
value: _switchValue,
onChanged: (value) => setState(() => _switchValue = value),
label: 'Enable notifications',
),
SizedBox(height: theme.spacing.md),
// Select dropdown
GarnishSelect<String>(
value: _selectedOption,
options: const [
GarnishSelectOption(value: 'option1', label: 'Option 1'),
GarnishSelectOption(value: 'option2', label: 'Option 2'),
GarnishSelectOption(value: 'option3', label: 'Option 3'),
],
onChanged: (value) => setState(() => _selectedOption = value),
placeholder: 'Select an option',
label: 'Dropdown',
),
SizedBox(height: theme.spacing.xxl),
// ============================================================
// CARD COMPONENT
// ============================================================
_SectionTitle(title: 'Card Component'),
SizedBox(height: theme.spacing.md),
GarnishCard(
animation: GarnishAnimation.fadeSlideUp(delay: const Duration(milliseconds: 100)),
child: Padding(
padding: EdgeInsets.all(theme.spacing.lg),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Card Title',
style: theme.typography.titleLarge.copyWith(
color: theme.colors.onSurface,
),
),
SizedBox(height: theme.spacing.sm),
Text(
'This is a card component with a title and description. Cards are versatile containers for content.',
style: theme.typography.bodyMedium.copyWith(
color: theme.colors.onSurface,
),
),
SizedBox(height: theme.spacing.md),
GarnishButton(
onPressed: () {},
size: GarnishButtonSize.small,
child: const Text('Learn More'),
),
],
),
),
),
SizedBox(height: theme.spacing.xxl),
// ============================================================
// STAGGERED ANIMATION DEMO
// ============================================================
_SectionTitle(title: 'Staggered Animations'),
SizedBox(height: theme.spacing.md),
Text(
'Items animate with increasing delays:',
style: theme.typography.bodyMedium.copyWith(
color: theme.colors.onBackground,
),
),
SizedBox(height: theme.spacing.md),
...List.generate(4, (index) {
return Padding(
padding: EdgeInsets.only(bottom: theme.spacing.sm),
child: GarnishCard(
animation: GarnishAnimation.fadeSlideUp().staggered(index),
child: Padding(
padding: EdgeInsets.all(theme.spacing.md),
child: Row(
children: [
GarnishAvatar(
initials: 'U${index + 1}',
size: GarnishAvatarSize.md,
),
SizedBox(width: theme.spacing.md),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'List Item ${index + 1}',
style: theme.typography.titleSmall.copyWith(
color: theme.colors.onSurface,
),
),
Text(
'Stagger delay: ${50 * index}ms',
style: theme.typography.bodySmall.copyWith(
color: theme.colors.onSurface,
),
),
],
),
),
],
),
),
),
);
}),
SizedBox(height: theme.spacing.xxl),
// ============================================================
// FEEDBACK COMPONENTS
// ============================================================
_SectionTitle(title: 'Feedback Components'),
SizedBox(height: theme.spacing.md),
GarnishAlert(
severity: GarnishAlertSeverity.info,
title: 'Information',
message: 'This is an informational alert message.',
),
SizedBox(height: theme.spacing.sm),
GarnishAlert(
severity: GarnishAlertSeverity.success,
title: 'Success',
message: 'Your changes have been saved successfully.',
),
SizedBox(height: theme.spacing.sm),
GarnishAlert(
severity: GarnishAlertSeverity.warning,
title: 'Warning',
message: 'Please review your input before continuing.',
),
SizedBox(height: theme.spacing.sm),
GarnishAlert(
severity: GarnishAlertSeverity.error,
title: 'Error',
message: 'Something went wrong. Please try again.',
),
SizedBox(height: theme.spacing.xxl),
// ============================================================
// DISPLAY COMPONENTS
// ============================================================
_SectionTitle(title: 'Display Components'),
SizedBox(height: theme.spacing.md),
Wrap(
spacing: theme.spacing.md,
runSpacing: theme.spacing.md,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
// Avatars
GarnishAvatar(
initials: 'JD',
size: GarnishAvatarSize.sm,
),
GarnishAvatar(
initials: 'AB',
size: GarnishAvatarSize.md,
),
GarnishAvatar(
initials: 'XY',
size: GarnishAvatarSize.lg,
),
// Badges
GarnishBadge(
label: 'New',
variant: GarnishBadgeVariant.filled,
),
GarnishBadge(
label: 'Beta',
variant: GarnishBadgeVariant.outlined,
),
GarnishBadge(
label: 'Deprecated',
variant: GarnishBadgeVariant.subtle,
),
],
),
SizedBox(height: theme.spacing.md),
// Progress indicators
GarnishProgress(
value: 0.7,
variant: GarnishProgressVariant.linear,
),
SizedBox(height: theme.spacing.xxl),
// Footer
Center(
child: Text(
'Garnish UI v0.3.0',
style: theme.typography.bodySmall.copyWith(
color: theme.colors.onBackground,
),
),
),
SizedBox(height: theme.spacing.lg),
],
),
);
}
String _styleCategoryName(GarnishStyleCategory category) {
switch (category) {
case GarnishStyleCategory.minimal:
return 'Minimal';
case GarnishStyleCategory.material:
return 'Material';
case GarnishStyleCategory.neumorphism:
return 'Neumorphism';
case GarnishStyleCategory.glassmorphism:
return 'Glassmorphism';
case GarnishStyleCategory.brutalism:
return 'Brutalism';
}
}
String _styleCategoryDescription(GarnishStyleCategory category) {
switch (category) {
case GarnishStyleCategory.minimal:
return 'Clean, flat design with subtle shadows and rounded corners.';
case GarnishStyleCategory.material:
return 'Elevated surfaces with depth, shadows, and Material Design principles.';
case GarnishStyleCategory.neumorphism:
return 'Soft, extruded appearance with inner and outer shadows.';
case GarnishStyleCategory.glassmorphism:
return 'Frosted glass effect with transparency and blur.';
case GarnishStyleCategory.brutalism:
return 'Bold, raw aesthetic with hard shadows and strong borders.';
}
}
}
/// A section title widget for organizing content.
class _SectionTitle extends StatelessWidget {
const _SectionTitle({required this.title});
final String title;
@override
Widget build(BuildContext context) {
final theme = GarnishTheme.of(context);
return Text(
title,
style: theme.typography.titleLarge.copyWith(
color: theme.colors.onBackground,
fontWeight: FontWeight.w600,
),
);
}
}