LogoSkills

CoUI Flutter Quick Reference

This project uses **`context.textStyles`** (used in 22 files).

๐Ÿ’ก See CLAUDE.md for the full guide. This document is a quick reference summary for CoUI component usage.

Typography API Guide#

Project Standard: context.textStyles#

This project uses context.textStyles (used in 22 files).

// โœ… Project standard: use textStyles
style: context.textStyles.lgSemibold
style: context.textStyles.smSemibold
style: context.textStyles.sm.merge(context.textStyles.medium)

// โœ… copyWith composition
style: context.textStyles.lgSemibold.copyWith(
  color: context.appColors.neutral5,
)

API Selection#

APIStatus
context.textStyles โœ… Project standard (used in 22 files)
context.typographyโŒ Prohibited (0 occurrences)

Combination Getters (Frequently Used)#

GetterCompositionExample
lgSemibold16px + 600 weightHeadings, emphasis text
smSemibold12px + 600 weightLabels, badges
baseMedium14px + 500 weightBody emphasis
sm12pxSecondary text
xs10pxCaption

Complete Font Size List#

GetterSizePurpose
xs10pxCaption, annotation
sm12pxSecondary text, labels
base14pxDefault body
lg16pxEmphasis body, subheading
xl20pxHeading
x2l24pxLarge heading
x3l30pxPage heading
x4l36pxExtra large heading
x5l48pxHero text

TextField features Parameter#

โš ๏ธ deprecated: The leading and trailing parameters are deprecated.

// โœ… New API: features parameter
TextField(
  controller: controller,
  placeholder: const Text('์ž…๋ ฅํ•ด์ฃผ์„ธ์š”'),
  filled: true,  // ๋ฐฐ๊ฒฝ์ƒ‰ ์ ์šฉ (base200)
  features: [
    InputFeature.leading(const Icon(Icons.search, size: 20)),
    InputFeature.trailing(const Icon(Icons.clear, size: 18)),
  ],
  onChanged: (value) => ...,
)

// โŒ deprecated: leading/trailing parameters
TextField(
  leading: Icon(Icons.search),   // deprecated
  trailing: Icon(Icons.clear),   // deprecated
)

TextField Key Properties#

PropertyTypeDescription
filled bool true applies background color (base200)
border Border Border setting (Border.all(color: context.colorScheme.base300))
borderRadiusBorderRadiusBorder radius
features List leading, trailing, clear, etc.

ButtonSize & ButtonDensity#

ButtonSize Options#

โš ๏ธ Note: ButtonSize.medium does not exist!

ConstantScalePurpose
ButtonSize.xSmall0.5xVery small button
ButtonSize.small0.75xSmall button
ButtonSize.normal1.0xDefault
ButtonSize.large2.0xLarge button
ButtonSize.xLarge3.0xVery large button
// โœ… CORRECT: Use existing sizes
ButtonStyle.primary(size: .normal)
ButtonStyle.primary(size: .small)

// โŒ WRONG: medium doesn't exist (compile error)
ButtonStyle.primary(size: .medium)  // ์—๋Ÿฌ!

ButtonDensity Options#

ConstantPurpose
ButtonDensity.normalNormal density
ButtonDensity.comfortableComfortable padding
ButtonDensity.dense Compact button (buttons in tables)
ButtonDensity.compactMinimum padding
ButtonDensity.iconIcon only
ButtonDensity.iconComfortableIcon + comfortable padding
ButtonDensity.iconDenseIcon + compact
// Compact button in table
Button.outline(
  style: const ButtonStyle.outline(density: .dense),
  onPressed: handleTap,
  child: const Text('๋ณต์‚ฌ'),
)

Badge Component#

Badge component for status display. Used with ButtonStyle.

Badge Types#

ComponentPurposeDefault Color
PrimaryBadgePositive status (on sale, active, etc.)primary
DestructiveBadge Negative status (stopped, deleted, etc.) destructive (red)

Usage Examples#

// โœ… Basic usage
const PrimaryBadge(child: Text('ํŒ๋งคin progress'))
const DestructiveBadge(child: Text('ํŒ๋งค in progress์ง€'))

// โœ… Make compact with density adjustment
const PrimaryBadge(
  style: ButtonStyle.primary(density: .dense),
  child: Text('ํŒ๋งคin progress'),
)

const DestructiveBadge(
  style: ButtonStyle.destructive(density: .dense),
  child: Text('ํŒ๋งค in progress์ง€'),
)

// โœ… Conditional badge
child: book.isActive
    ? const PrimaryBadge(
        style: ButtonStyle.primary(density: .dense),
        child: Text('ํŒ๋งคin progress'),
      )
    : const DestructiveBadge(
        style: ButtonStyle.destructive(density: .dense),
        child: Text('ํŒ๋งค in progress์ง€'),
      ),

Gap & Insets Constants#

Gap Usage#

const Gap.s1()        // 4px
const Gap.s2()        // 8px
const Gap.s4()        // 16px
Gap(Insets.small)     // 8px
Gap(Insets.medium)    // 16px
Gap(Insets.large)     // 24px

Insets Constants Table#

ConstantValuePurpose
Insets.xSmall4pxIcon spacing
Insets.small8pxElement spacing
Insets.medium16pxSection padding
Insets.large24pxCard padding
Insets.xLarge32pxPage margin

Loading/Skeleton Patterns#

// Skeleton loading (entire widget)
widget.skeletonizer(enabled: state.isLoading)

// Conditional loading (spinner)
widget.loadingOr(
  isLoading: state.isLoading,
  loadingWidget: const CircularProgressIndicator(),
)

// Empty state handling
listWidget.emptyOrWhen(
  condition: () => list.isEmpty,
  emptyWidget: const EmptyStateWidget(),
)

Complete Page Example#

class MyPage extends HookWidget {
  const MyPage({super.key});

  @override
  Widget build(BuildContext context) {
    final appColors = context.appColors;

    return BlocProvider(
      create: (_) => MyBloc(),
      child: BlocBuilder<MyBloc, MyState>(
        builder: (context, state) {
          return Scaffold(
            headers: [
              AppBar(
                title: Text(
                  'ํŽ˜์ด์ง€ ์ œ๋ชฉ',
                  style: context.textStyles.lgSemibold.copyWith(
                    color: appColors.neutral5,
                  ),
                ),
              ),
            ],
            child: Padding(
              padding: const .symmetric(horizontal: Insets.medium),
              child: Column(
                crossAxisAlignment: .start,
                children: [
                  const Gap(Insets.medium),
                  TextField(
                    placeholder: const Text('์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”'),
                    features: const [
                      InputFeature.leading(Icon(Icons.person, size: 20)),
                    ],
                    onChanged: (value) => context.read<MyBloc>().add(
                      MyEvent.nameChanged(value),
                    ),
                  ),
                  const Gap(Insets.medium),
                  Button.primary(
                    expanded: true,
                    enabled: state.isValid,
                    onPressed: () => context.read<MyBloc>().add(
                      const MyEvent.submitted(),
                    ),
                    child: const Text('์ €์žฅ'),
                  ),
                ],
              ),
            ).skeletonizer(enabled: state.isLoading),
          );
        },
      ),
    );
  }
}

Color Scheme (Console UI)#

AppColors Neutral Series#

ColorHEXPurpose
neutral100#FFFFFFWhite background
neutral95 #F9FAFB Header/section background (distinct from body)
neutral85#F4F5F6Light gray
neutral80#ECEEEFDefault border
neutral70 #B4B8C4 Dark border (clear separator)
neutral60#8F929AInactive text
neutral30-Body text
// Header background (distinct from body)
color: context.appColors.neutral95,

// Dark border (clear separation)
border: Border(
  right: .new(color: context.appColors.neutral70),
),

// Default border
border: Border.all(color: context.appColors.neutral80),

ColorScheme Base Series#

CoUI's color scheme.

ColorPurpose
base100Lightest background
base200Medium background (filled TextField)
base300Darkest background/border
// TextField background
TextField(
  filled: true,  // base200 ๋ฐฐ๊ฒฝ
  border: Border.all(color: context.colorScheme.base300),
)

// Filter dialog border
OutlinedContainer(
  backgroundColor: theme.colorScheme.base100,
  borderColor: theme.colorScheme.base300,  // ์ง„ํ•œ ํ…Œ๋‘๋ฆฌ
)

Reference Documents#

Refer to CLAUDE.md for the following:

  • Color rules (AppColors, context.appColors)
  • Dot Shorthand rules (Dart 3.10+)
  • Button/IconButton usage
  • BLoC Event/State patterns (sealed class)
  • BLoC async handlers (isClosed check)
  • Complete list of Context Extensions