Icon#
CoUI 디자인 시스템의 통일된 아이콘 컴포넌트입니다. Iconify 생태계(Lucide, Radix, Bootstrap, HeroIcons) 아이콘을 IconName
참조로 렌더합니다. Flutter와 Web이 동일한 API와 동일한 아이콘 레퍼런스를 사용합니다.
앱은 네 카탈로그를 자유롭게 쓸 수 있지만, CoUI 자신의 컴포넌트는
LucideIcons하나만 씁니다. 각 세트는 자기 그리드와 자기 획 두께로 그려져, 같은size를 요청해도 시각적 무게가 다르게 도착하기 때문입니다 — 한 벌에서 온 체크와 다른 벌에서 온 chevron 이 나란히 있으면 선택이 아니라 렌더 버그로 읽힙니다. 한 앱 안에서 섞을 때도 같은 이유로 인접한 컨트롤끼리는 한 세트로 맞추는 편이 좋습니다.
Live Preview#
class IconDefaultExample extends StatelessComponent {
const IconDefaultExample({super.key});
@override
Component build(BuildContext context) {
return const Icon(LucideIcons.house, iconStyle: CoreIconStyle(size: 28));
}
}
class IconDefaultExample extends StatelessWidget {
const IconDefaultExample({super.key});
@override
Widget build(BuildContext context) {
return const Icon(LucideIcons.house, iconStyle: CoreIconStyle(size: 28));
}
}
class IconSearchExample extends StatelessComponent {
const IconSearchExample({super.key});
@override
Component build(BuildContext context) {
return const Icon(LucideIcons.search, iconStyle: CoreIconStyle(size: 28));
}
}
class IconSearchExample extends StatelessWidget {
const IconSearchExample({super.key});
@override
Widget build(BuildContext context) {
return const Icon(LucideIcons.search, iconStyle: CoreIconStyle(size: 28));
}
}
class IconFavoriteExample extends StatelessComponent {
const IconFavoriteExample({super.key});
@override
Component build(BuildContext context) {
return Icon(
LucideIcons.heart,
iconStyle: CoreIconStyle(size: 28, color: context.colorScheme.tertiary),
);
}
}
class IconFavoriteExample extends StatelessWidget {
const IconFavoriteExample({super.key});
@override
Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme;
return Icon(
LucideIcons.heart,
iconStyle: CoreIconStyle(size: 28, color: scheme.tertiary.toValue()),
);
}
}
class IconChainExample extends StatelessComponent {
const IconChainExample({super.key});
@override
Component build(BuildContext context) {
return const Icon(
LucideIcons.house,
iconStyle: CoreIconStyle(size: 28),
).withStyle(
const CoreIconStyle(
size: CoreIconSize.size32,
color: CoreColor.token(CoreColors.primary),
),
);
}
}
class IconChainExample extends StatelessWidget {
const IconChainExample({super.key});
@override
Widget build(BuildContext context) {
return const Icon(
LucideIcons.house,
iconStyle: CoreIconStyle(size: 28),
).withStyle(
const CoreIconStyle(
size: CoreIconSize.size32,
color: CoreColor.token(CoreColors.primary),
),
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 버튼, 메뉴, 네비게이션 항목에 시각적 힌트가 필요할 때
- 텍스트 없이 아이콘만으로 의미를 전달해야 할 때 (접근성 레이블 필수)
- CoUI 디자인 시스템의 통일된 아이콘 세트를 사용할 때
대신 다른 컴포넌트를 사용하세요:
Badge: 아이콘 위에 숫자나 상태 표시가 필요할 때Icon과Badge를 조합Fab: 아이콘 버튼을 강조된 액션 버튼으로 표시할 때
기본 사용법 (Basic Usage)#
Icon의 모든 시각 커스터마이징은 iconStyle: CoreIconStyle(...)로 단일화되어 있습니다.
// 기본 아이콘 (디자인 시스템 default — 24px / onSurface)
const Icon(LucideIcons.house);
// 크기 지정
const Icon(
LucideIcons.house,
iconStyle: CoreIconStyle(size: 28),
);
// 다른 아이콘 세트
const Icon(RadixIcons.chevronLeft, iconStyle: CoreIconStyle(size: 16));
const Icon(BootstrapIcons.gear, iconStyle: CoreIconStyle(size: 20));
// HeroIcons 스타일 선택
const Icon(HeroIcons.arrowLeft.outline, iconStyle: CoreIconStyle(size: 24));
const Icon(HeroIcons.arrowLeft.solid, iconStyle: CoreIconStyle(size: 24));
// 크기 + 색상
Icon(
LucideIcons.heart,
iconStyle: CoreIconStyle(
size: 28,
color: Theme.of(context).colorScheme.tertiary,
),
);
// 같은 스타일을 여러 인스턴스에 재사용
final dangerIcon = CoreIconStyle(
size: 20,
color: Theme.of(context).colorScheme.error,
);
Icon(LucideIcons.alertTriangle, iconStyle: dangerIcon);
Icon(LucideIcons.x, iconStyle: dangerIcon);
// 기본 아이콘
const Icon(LucideIcons.house);
// 크기 지정
const Icon(
LucideIcons.house,
iconStyle: CoreIconStyle(size: 28),
);
// 다른 아이콘 세트
const Icon(RadixIcons.chevronLeft, iconStyle: CoreIconStyle(size: 16));
const Icon(BootstrapIcons.gear, iconStyle: CoreIconStyle(size: 20));
// HeroIcons 스타일 선택
const Icon(HeroIcons.arrowLeft.outline, iconStyle: CoreIconStyle(size: 24));
const Icon(HeroIcons.arrowLeft.solid, iconStyle: CoreIconStyle(size: 24));
// 크기 + 색상 — Web 의 color 는 Tailwind colour token 문자열
Icon(
LucideIcons.heart,
iconStyle: CoreIconStyle(
size: 28,
color: context.colorScheme.tertiary, // 'tertiary' 토큰
),
);
빠른 오버라이드 (Chain)#
이미 만든 Icon 인스턴스에 스타일을 빠르게 덧붙이고 싶다면 withStyle 체인을 쓸 수 있습니다. 생성자의 iconStyle
인자와 동일하게 동작하지만, 이미 구성된 위젯 위에서 바로 이어 쓸 수 있습니다.
class IconChainExample extends StatelessWidget {
const IconChainExample({super.key});
@override
Widget build(BuildContext context) {
return const Icon(
LucideIcons.house,
iconStyle: CoreIconStyle(size: 28),
).withStyle(
const CoreIconStyle(
size: CoreIconSize.size32,
color: CoreColor.token(CoreColors.primary),
),
);
}
}
class IconChainExample extends StatelessComponent {
const IconChainExample({super.key});
@override
Component build(BuildContext context) {
return const Icon(
LucideIcons.house,
iconStyle: CoreIconStyle(size: 28),
).withStyle(
const CoreIconStyle(
size: CoreIconSize.size32,
color: CoreColor.token(CoreColors.primary),
),
);
}
}
Props / Parameters#
| 속성 | 타입 (Flutter) | 타입 (Web) | 기본값 | 설명 |
|---|---|---|---|---|
icon |
IconName |
IconName |
필수 (positional) | 표시할 아이콘 (LucideIcons.* 등) |
iconStyle |
CoreIconStyle? |
CoreIconStyle? |
null |
아이콘 크기/색상 (Style 시스템 참조) |
스타일 시스템 (Style System)#
Icon 의 모든 시각 오버라이드는 단일 iconStyle (CoreIconStyle) 으로 흐릅니다. IconName
식별자만 위젯 파라미터로 직접 전달합니다.
CoreIconStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
size |
double? |
Icon size in logical pixels (Flutter) / CSS pixels (Web). null → inherit. |
color |
CoreColor? |
Icon colour. null → inherit. |
Resolve chain#
design system default
→ CoreIconTheme.style // 프로젝트 공통
→ parent component slot override // 예: CoreButtonStyle.leadingIconStyle
→ widget.iconStyle // 인스턴스별
각 레이어는 CoreIconStyle 의 어떤 필드든 부분적으로 채울 수 있습니다. null 인 필드는 다음 레이어로 넘겨주고, non-null 인 필드는 해당 레이어에서 확정됩니다.
색상 처리 차이점#
-
Flutter:
Color객체로 받아ColorFilter.mode(color, BlendMode.srcIn)으로 tint -
Web: Tailwind colour token 문자열(
'tertiary'등)로 받아text-${token}class 적용 - 양쪽 모두
currentColor기반 SVG를 tint하는 결과는 동일
테마 연동#
프로젝트 레벨 기본값은 CoreComponentTheme.icon에 CoreIconTheme.style로 지정합니다.
CoUIApp(
theme: ThemeData.fromCore(
CoreThemePresets.light,
coreComponentTheme: const CoreComponentTheme(
icon: CoreIconTheme(
style: CoreIconStyle(
size: 20,
color: CoreColor.token(CoreColors.onSurfaceVariant),
),
),
),
),
home: MyHomePage(),
)
이후 iconStyle이 null이거나 size/color가 null인 모든 Icon은 위 default를 따릅니다.
크기 권장값#
| 용도 | 크기 |
|---|---|
| 인라인 텍스트 | 16.0 |
| 버튼 내 아이콘 | 20.0 |
| 기본 아이콘 | 24.0 |
| 강조 아이콘 | 32.0 |
| 히어로 아이콘 | 48.0 |
사용 가이드라인 (Usage Guidelines)#
✅ Do#
색상을 생략해 부모 색을 자동으로 상속
Icon(LucideIcons.check)
iconStyle.color를 생략하면 Web은 currentColor로 부모 색을 상속하고 Flutter는 ambient IconTheme을 따릅니다 — 배경색이 다른 버튼/배지 안에 넣어도 매번 색을 새로 지정할 필요 없이 자동으로 어울리는 색이 됩니다.
❌ Don't#
한 화면 안에서 아이콘 세트를 섞어 쓰지 않기
// ❌ 인접한 아이콘에 서로 다른 세트 사용
Row(
children: [
Icon(LucideIcons.chevronRight),
Icon(RadixIcons.chevronRight),
],
)
각 세트는 자기 그리드와 획 두께로 그려져 같은 size를 줘도 시각적 무게가 다르게 도착합니다 — 인접한 컨트롤끼리 세트가 섞이면 선택이 아니라 렌더 버그로 보입니다. CoUI 컴포넌트 내부는 물론, 앱 코드에서도 인접한 아이콘끼리는 LucideIcons 한 세트로 통일하세요.
접근성 (Accessibility)#
- 아이콘은 시각적 요소이므로, 텍스트 레이블이 없는 독립 아이콘에는 tooltip 또는 부모
aria-label을 제공하세요 - Flutter:
Semanticswrapper 사용 -
Web:
<iconify-icon>은 기본적으로aria-hidden상태 — 의미 전달이 필요하면 부모 요소에aria-label지정