Skip to main content

Primitives

Primitive components are the fundamental building blocks of the BookWish UI. These components are used throughout the application and form the foundation for more complex components.

Buttons

BookWish uses three primary button variants, each with distinct visual styles and use cases.

ElevatedButton (Primary)

The primary call-to-action button with Amber Star background.

Visual Style:

  • Background: Amber Star (#FFC857)
  • Text Color: Ink Blue (#233548)
  • Height: 48px minimum
  • Border Radius: 24px (fully rounded)
  • Elevation: 0 (flat design)
  • Full width by default

Usage:

  • Primary actions (Sign Up, Save, Submit)
  • One primary button per screen section
  • Most important user action

Props:

ElevatedButton(
onPressed: () => // action,
child: Text('Button Label'),
)

Example:

ElevatedButton(
onPressed: () => handleSubmit(),
child: const Text('Add to Wishlist'),
)

OutlinedButton (Secondary)

Secondary actions with Ink Blue outline.

Visual Style:

  • Background: Transparent
  • Border: 1px Ink Blue (#233548)
  • Text Color: Ink Blue (#233548)
  • Height: 48px minimum
  • Border Radius: 24px (fully rounded)
  • Full width by default

Usage:

  • Secondary actions (Cancel, Skip)
  • Alternative actions paired with primary button
  • Less critical actions

Props:

OutlinedButton(
onPressed: () => // action,
child: Text('Button Label'),
)

Example:

OutlinedButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
)

Special Variant - Follow Button:

The FollowButton component uses OutlinedButton with dynamic styling:

// Following state
backgroundColor: surfaceContainerHighest
foregroundColor: onSurface
text: "Following"

// Not following state
backgroundColor: primary (Ink Blue)
foregroundColor: onPrimary (White)
text: "Follow"

TextButton (Tertiary)

Minimal button for tertiary actions.

Visual Style:

  • Background: Transparent
  • Text Color: Ink Blue (#233548)
  • No border
  • Height: Auto-sized to content
  • No background or border

Usage:

  • Tertiary actions (Learn More, See All)
  • Navigation within dialogs
  • Less prominent actions

Props:

TextButton(
onPressed: () => // action,
child: Text('Button Label'),
)

Example:

TextButton(
onPressed: () => showHelp(),
child: const Text('Learn More'),
)

Button States

All buttons support standard Flutter states:

  • Enabled: Default appearance
  • Disabled: onPressed: null - Reduced opacity
  • Pressed: Material ripple effect
  • Loading: Show CircularProgressIndicator

Loading State Example:

ElevatedButton(
onPressed: isLoading ? null : handleSubmit,
child: isLoading
? const CircularProgressIndicator()
: const Text('Submit'),
)

Text Fields

Input fields for user data entry with consistent styling.

Visual Style:

  • Background: White (#FFFFFF)
  • Border: 1px Border color (#E0D7C8)
  • Border Radius: 12px
  • Padding: 16px horizontal and vertical
  • Focus Border: 2px Ink Blue (#233548)
  • Error Border: 1px Error Red (#C44545)
  • Focus Error Border: 2px Error Red (#C44545)

Usage:

  • Text input
  • Email input
  • Password input
  • Search fields
  • Any user text entry

Props:

TextField(
decoration: InputDecoration(
labelText: 'Label',
hintText: 'Hint text',
errorText: 'Error message',
),
controller: controller,
onChanged: (value) => // handle change,
)

Example:

TextField(
decoration: const InputDecoration(
labelText: 'Email',
hintText: 'Enter your email address',
),
keyboardType: TextInputType.emailAddress,
controller: emailController,
)

Text Field States

  • Default: Border color border
  • Focused: Ink Blue border, 2px width
  • Error: Error Red border
  • Disabled: Reduced opacity

Chips

Compact elements for tags, filters, and metadata display.

StoreChip

Display store information compactly.

Visual Style:

  • Background: surfaceContainerHighest (light gray)
  • Padding: 12px horizontal, 6px vertical
  • Border Radius: 16px (pill shape)
  • Icon Size: 20px
  • Text: AppTypography.labelSmall with medium weight

Usage:

  • Store identification
  • Store badges
  • Store references in lists

Props:

final Store store;
final VoidCallback? onTap;

Example:

StoreChip(
store: bookstore,
onTap: () => navigateToStore(bookstore.id),
)

Generic Chip Pattern

For creating other chip types:

Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, size: 20),
const SizedBox(width: 6),
Text(label, style: AppTypography.labelSmall),
],
),
)

Cards

Content containers with consistent styling throughout the app.

Visual Style:

  • Background: White (#FFFFFF)
  • Elevation: 2
  • Shadow: Black at 8% opacity
  • Border Radius: 12px
  • Border: 1px Border color (#E0D7C8) on ListTile variant
  • Horizontal Margin: 16px
  • Vertical Margin: 6px between cards

Standard Card Pattern

Used in most card components (ReviewCard, ClubCard, etc.):

Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: Material(
elevation: 2,
shadowColor: Colors.black.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(12),
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(16),
child: // Card content
),
),
)

ListTile Card Pattern

Used in tile-based components (InventoryItemTile, WishlistItemTile):

Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: Material(
elevation: 2,
shadowColor: Colors.black.withValues(alpha: 0.08),
borderRadius: BorderRadius.circular(12),
color: Colors.white,
child: ListTile(
// ListTile configuration
),
),
)

Usage:

  • Container for related content
  • Grouping information
  • Interactive content blocks
  • List items

Card Content Styling

Content within cards uses card-specific typography:

// Heading
Text(heading, style: AppTypography.cardHeading)

// Body text
Text(content, style: AppTypography.cardBody)

// Labels
Text(label, style: AppTypography.cardLabel)

// Captions and metadata
Text(metadata, style: AppTypography.cardCaption)

Avatars

User profile images in circular containers.

Visual Style:

  • Shape: Circle (CircleAvatar)
  • Fallback: User initial on colored background
  • Fallback Text: AppTypography.cardLabel

Sizes

Standard Size (32px radius)

CircleAvatar(
radius: 16,
backgroundImage: user.avatarUrl != null
? NetworkImage(user.avatarUrl!)
: null,
child: user.avatarUrl == null
? Text(
(user.displayName ?? 'U')[0].toUpperCase(),
style: AppTypography.cardLabel.copyWith(fontSize: 12),
)
: null,
)

Usage:

  • User identification in UserChip
  • Comment and review authors
  • Follower/following lists
  • User profile references

Avatar Fallback

When no image is available:

  • Display first letter of username or display name
  • Use card label typography
  • Size appropriate to container

Toggles

Boolean controls for settings and preferences.

Visual Style:

  • Uses Flutter's Switch widget
  • Active Color: Teal Edge (#4BB4C8)
  • Inactive Track: Light gray
  • Standard Material Design appearance

Usage:

  • Settings toggles
  • Feature enable/disable
  • Preference controls
  • Binary state changes

Example:

Switch(
value: isEnabled,
onChanged: (value) => setState(() => isEnabled = value),
)

In List Context:

SwitchListTile(
title: const Text('Enable Notifications'),
value: notificationsEnabled,
onChanged: (value) => updateNotificationSetting(value),
)

Loading Indicators

Provide feedback during asynchronous operations.

CircularProgressIndicator

Standard loading spinner.

Visual Style:

  • Primary Color: Ink Blue (#233548)
  • Standard Material Design appearance
  • Various sizes for different contexts

Usage:

  • Page loading states
  • Button loading states
  • Data fetching
  • Any asynchronous operation

Full Page Loading:

Center(
child: CircularProgressIndicator(),
)

Button Loading:

SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)

Inline Loading:

CircularProgressIndicator()

Image Loading

For network images, use CachedNetworkImage with placeholder:

CachedNetworkImage(
imageUrl: imageUrl,
placeholder: (context, url) => const Center(
child: CircularProgressIndicator(),
),
errorWidget: (context, url, error) => Icon(fallbackIcon),
)

Rating

Star-based rating display and input.

Display-Only Rating

Used in ReviewCard to show ratings:

Visual Style:

  • Icon: CupertinoIcons.star_fill (filled) / CupertinoIcons.star (empty)
  • Color: Amber (#FFC107 - Flutter's amber)
  • Size: 18px
  • Layout: Horizontal row of 5 stars

Implementation:

class _RatingStars extends StatelessWidget {
final int rating;

const _RatingStars({required this.rating});


Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(5, (index) {
return Icon(
index < rating ? CupertinoIcons.star_fill : CupertinoIcons.star,
size: 18,
color: Colors.amber,
);
}),
);
}
}

Usage:

  • Review ratings
  • Book ratings
  • Rating displays
  • Read-only rating visualization

Interactive Rating

For rating input, create tappable stars:

Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(5, (index) {
return IconButton(
icon: Icon(
index < currentRating
? CupertinoIcons.star_fill
: CupertinoIcons.star,
color: Colors.amber,
),
onPressed: () => setRating(index + 1),
);
}),
)

Icon Usage

BookWish primarily uses Cupertino (iOS-style) icons for a modern, clean aesthetic.

Common Icons

Books:

  • CupertinoIcons.book_fill - Book representation
  • CupertinoIcons.book - Outlined book

Social:

  • CupertinoIcons.heart_fill - Liked state
  • CupertinoIcons.heart - Unliked state
  • CupertinoIcons.person_3_fill - Book clubs, groups
  • CupertinoIcons.person_2_fill - Users, members

Actions:

  • CupertinoIcons.chevron_right - Navigation forward
  • CupertinoIcons.ellipsis - More options menu
  • CupertinoIcons.flag - Report/flag content
  • CupertinoIcons.trash_fill - Delete action

Status:

  • CupertinoIcons.star_fill - Featured, priority, ratings
  • CupertinoIcons.star - Unfilled star
  • CupertinoIcons.check_mark_circled_solid - Completed
  • CupertinoIcons.exclamationmark - High priority

Store:

  • CupertinoIcons.building_2_fill - Store/business

Content:

  • CupertinoIcons.doc_text_fill - Notes (with content)
  • CupertinoIcons.doc_text - Notes (empty)

Icon Sizing

  • Small: 16-18px - In-line with text, metadata
  • Medium: 20-24px - Standard icon buttons
  • Large: 32-48px - Empty states, placeholders

Spacing System

BookWish uses a consistent spacing system based on 4px increments.

Standard Spacing Values

  • 4px: Minimal spacing, tight groupings
  • 6px: Vertical spacing between cards
  • 8px: Small gaps, inline elements
  • 12px: Medium gaps, related elements
  • 16px: Standard padding, horizontal margins
  • 24px: Large gaps, section separation

Common Patterns

Card Padding:

padding: const EdgeInsets.all(16)

Card Margins:

padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6)

Element Spacing:

const SizedBox(height: 8)
const SizedBox(width: 12)

Border Radius System

Consistent corner rounding throughout the application.

Standard Values

  • 4px: Small images, thumbnails
  • 8px: Nested containers, mini chips
  • 12px: Cards, containers, text fields
  • 16px: Chips, tags
  • 20px: Ink wells, interactive feedback
  • 24px: Buttons (fully rounded)

Usage

Cards:

borderRadius: BorderRadius.circular(12)

Buttons:

borderRadius: BorderRadius.circular(24)

Chips:

borderRadius: BorderRadius.circular(16)

Elevation System

Two-level elevation system for depth and hierarchy.

Elevation Levels

Level 0: Flat elements on Parchment background

  • Buttons (elevation: 0)
  • Flat surfaces

Level 2: Cards and interactive elements

  • Cards (elevation: 2)
  • Tiles (elevation: 2)
  • Floating action buttons

Shadow Styling

Standard shadow for elevated elements:

shadowColor: Colors.black.withValues(alpha: 0.08)

This creates a subtle shadow that adds depth without overwhelming the design.

Accessibility

All primitive components follow accessibility best practices:

Touch Targets

Minimum touch target size: 44x44 points

  • Buttons: 48px minimum height
  • IconButtons: 44x44 minimum
  • Interactive elements: Adequate padding

Color Contrast

All text meets WCAG AA standards:

  • Ink Blue on Parchment: Sufficient contrast
  • Ink Blue on White: Excellent contrast
  • White on Ink Blue: Excellent contrast
  • Disabled states: Clearly distinguishable

Semantic Labels

Provide meaningful labels for interactive elements:

IconButton(
icon: Icon(CupertinoIcons.heart),
onPressed: onLike,
tooltip: 'Like this review',
)

Keyboard Navigation

Support keyboard navigation where applicable:

  • Tab through interactive elements
  • Enter/Space to activate buttons
  • Escape to dismiss modals

Best Practices

Do's

  • Use primitives consistently throughout the app
  • Follow the established spacing system
  • Provide loading and error states
  • Include proper accessibility labels
  • Use AppColors and AppTypography constants
  • Test with different content lengths

Don'ts

  • Don't create custom button styles
  • Don't hardcode spacing values outside the system
  • Don't skip accessibility considerations
  • Don't assume text fits on one line
  • Don't use colors outside the palette
  • Don't skip loading states for async operations