Card#
관련 정보를 하나의 단위로 묶어 표시하는 컨테이너 컴포넌트입니다. media/header/body/footer/overlay 슬롯을 제공합니다.
Live Preview#
class CardDefaultExample extends StatelessComponent {
const CardDefaultExample({super.key});
@override
Component build(BuildContext context) {
return CoCard(
header: Component.text('Card Title'),
body: Component.text('This is the card body content.'),
footer: CoButton(
variant: CoreButtonVariant.primary,
onPressed: () {},
child: Component.text('Action'),
),
);
}
}
class CardDefaultExample extends StatelessWidget {
const CardDefaultExample({super.key});
@override
Widget build(BuildContext context) {
return CoCard(
header: const Text('Card Title'),
body: const Text('This is the card body content.'),
footer: CoButton(
variant: CoreButtonVariant.primary,
onPressed: () {},
child: const Text('Action'),
),
);
}
}
class CardFilledExample extends StatelessComponent {
const CardFilledExample({super.key});
@override
Component build(BuildContext context) {
return CoCard(
variant: CoreComponentVariant.filled,
header: Component.text('Filled Card'),
body: Component.text('Filled variant with surface container background.'),
);
}
}
class CardFilledExample extends StatelessWidget {
const CardFilledExample({super.key});
@override
Widget build(BuildContext context) {
return const CoCard(
variant: CoreComponentVariant.filled,
header: Text('Filled Card'),
body: Text('Filled variant with surface container background.'),
);
}
}
class CardElevatedExample extends StatelessComponent {
const CardElevatedExample({super.key});
@override
Component build(BuildContext context) {
return CoCard(
elevation: CoreCardElevation.medium,
header: Component.text('Elevated Card'),
body: Component.text('Card with medium shadow elevation.'),
);
}
}
class CardElevatedExample extends StatelessWidget {
const CardElevatedExample({super.key});
@override
Widget build(BuildContext context) {
return const CoCard(
elevation: CoreCardElevation.medium,
header: Text('Elevated Card'),
body: Text('Card with medium shadow elevation.'),
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 관련 정보를 하나의 단위로 묶어 표시할 때 (프로필, 상품, 게시물 등)
- 시각적으로 콘텐츠를 구분하고 계층을 만들 때
- 클릭 가능한 콘텐츠 블록이 필요할 때
대신 다른 컴포넌트를 사용하세요:
Accordion: 접고 펼 수 있는 콘텐츠 섹션이 필요할 때Table: 구조화된 데이터를 행과 열로 표시할 때Dialog: 임시 콘텐츠를 모달로 표시할 때
기본 사용법 (Basic Usage)#
// 기본 카드 (outlined, no elevation)
CoCard(
body: Text('카드 내용'),
)
// 슬롯 기반 레이아웃
CoCard(
header: Text('제목'),
body: Text('본문 내용입니다.'),
footer: Text('푸터'),
)
// variant + elevation (chrome / dimensional 은 cardStyle 로 통합)
CoCard(
variant: CoreComponentVariant.filled,
cardStyle: CoreCardStyle(
elevation: CoreCardElevation.medium,
),
body: Text('강조된 카드'),
)
// 클릭 가능
CoCard(
onTap: () => print('clicked'),
body: Text('Click me'),
)
스타일 시스템 — cardStyle (Epic #1302)#
CoCard 의 모든 chrome / dimensional 오버라이드는 단일 cardStyle 필드(CoreCardStyle)
하나로 흐릅니다. 이전 버전의 flat 필드(width, padding, boxShadow,
elevation 등)는
모두 CoreCardStyle 안의 nullable 필드로 옮겨졌습니다.
CoCard(
variant: CoreComponentVariant.outlined,
cardStyle: CoreCardStyle(
padding: EdgeInsets.all(CoreSpace.space24),
borderRadius: BorderRadius.circular(12),
backgroundColor: theme.colorScheme.surfaceContainer,
elevation: CoreCardElevation.soft,
sizing: CoreCardSizing.fixed,
width: 320,
),
header: Text('제목'),
body: Text('본문'),
)
머지 체인 (Merge Chain)#
cardStyle 은 다음 체인을 거쳐 최종 값으로 해석됩니다 (오른쪽이 이김):
design system default
→ CoreCardTheme.style // 프로젝트 공통
→ CoreCardTheme.variantStyles[variant] // variant 별 오버라이드
→ 부모 컴포넌트 슬롯 오버라이드 // 예: TimePicker.popoverPanelStyle
→ widget.cardStyle // 인스턴스별 오버라이드
각 레이어는 CoreCardStyle 의 어떤 필드든 부분적으로 채울 수 있습니다. null 인 필드는
다음 레이어로 넘겨주고, non-null 인 필드는 해당 레이어에서 확정됩니다.
테마 적용#
ThemeData.fromCore(
coreComponentTheme: CoreComponentTheme(
card: CoreCardTheme(
style: CoreCardStyle(
// 모든 카드에 적용되는 기본
borderRadius: BorderRadius.circular(16),
padding: EdgeInsets.all(CoreSpace.space20),
),
variantStyles: {
CoreComponentVariant.filled: CoreCardStyle(
backgroundColor: theme.colorScheme.primaryContainer,
),
},
),
),
)
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
variant |
CoreComponentVariant |
.outlined |
시각적 variant (filled / outlined) |
media |
Widget? / Component? |
null |
상단 미디어 영역 (이미지 등) |
header |
Widget? / Component? |
null |
헤더 영역 |
body |
Widget? / Component? |
null |
본문 영역 |
footer |
Widget? / Component? |
null |
푸터 영역 |
overlay |
Widget? / Component? |
null |
media 위에 오버레이 |
child |
Widget? / Component? |
null |
커스텀 자식 (slot 대신) |
onTap |
VoidCallback? |
null |
탭/클릭 핸들러 |
cardStyle |
CoreCardStyle? |
null |
chrome + dimensional 오버라이드 묶음 (아래 표 참고) |
CoreCardStyle 필드#
| 필드 | 타입 | 설명 |
|---|---|---|
padding |
EdgeInsetsGeometry? / double? |
내부 패딩 |
borderRadius |
BorderRadiusGeometry? / double? |
보더 반경 |
borderWidth |
double? |
보더 두께 (logical px, pre-scaling) |
backgroundColor | Color? / String? | 배경색 |
borderColor | Color? / String? | 보더 색상 |
boxShadow |
List<BoxShadow>? / String? |
커스텀 그림자 (elevation 보다 우선) |
headerBodyGap | double? | 헤더-바디 간격 (logical px) |
bodyFooterGap | double? | 바디-푸터 간격 (logical px) |
duration | Duration? | 전환 애니메이션 지속 시간 |
width |
double? |
고정 너비 (sizing이 .fixed 일 때) |
height |
double? |
고정 높이 (sizing이 .fixed 일 때) |
sizing |
CoreCardSizing? |
크기 결정 방식 (intrinsic / expand / fixed) |
elevation |
CoreCardElevation? |
그림자 수준 (none / soft / medium / strong) |
clipBehavior | Clip? / bool? | 클리핑 동작 |
변형 (Variants)#
Outlined (기본)#
CoCard(
variant: CoreComponentVariant.outlined,
body: Text('외곽선만'),
)
Filled#
CoCard(
variant: CoreComponentVariant.filled,
body: Text('배경색 채움'),
)
Elevation 단계#
CoCard(
cardStyle: CoreCardStyle(elevation: CoreCardElevation.none),
body: Text('평면'),
)
CoCard(
cardStyle: CoreCardStyle(elevation: CoreCardElevation.soft),
body: Text('약한 그림자'),
)
CoCard(
cardStyle: CoreCardStyle(elevation: CoreCardElevation.medium),
body: Text('중간 그림자'),
)
CoCard(
cardStyle: CoreCardStyle(elevation: CoreCardElevation.strong),
body: Text('강한 그림자'),
)
미디어 카드 (이미지 + 콘텐츠)#
CoCard(
media: Image.network('https://example.com/photo.jpg'),
header: Text('상품명'),
body: Text('상품 설명'),
footer: Text('가격'),
)
동작 스펙 (Behavior)#
인터랙션#
- 탭/클릭:
onTap설정 시 전체 카드 영역 클릭 가능 - 호버 (onTap 있을 때): 커서가 pointer 로 변경 + hover shadow 애니메이션
애니메이션#
elevation전환: 200ms (CoreDuration.normal)- hover shadow:
cardStyle.duration(기본 200ms)
접근성 (Accessibility)#
시맨틱#
- Flutter: 기본
Container(onTap 있을 때MouseRegion + GestureDetector) - Web:
<div>(onTap 있을 때role="button",tabindex="0")
키보드#
onTap설정 시Enter/Space로 활성화 가능
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | CoCard | CoCard |
| 구조 | slot 기반 (media / header / body / footer / overlay) | slot 기반 (동일) |
| 스타일 제네릭 |
CoreCardStyle<Color, EdgeInsetsGeometry, BorderRadiusGeometry, List<BoxShadow>, Clip>
|
CoreCardStyle<String, double, double, String, bool> |
| 그림자 | CoreCardElevation.* (BoxShadow) |
CoreCardElevation.* (CSS shadow class) |
| 테마 | CoreCardTheme (Flutter 형) |
CoreCardTheme (Web 형) |
마이그레이션 (Epic #1302)#
이전 버전에서는 chrome / dimensional 필드가 위젯의 flat 파라미터였습니다. 이제 모두
cardStyle: CoreCardStyle(...) 안으로 이동했습니다.
// Before — flat 필드
CoCard(
width: 240,
padding: EdgeInsets.all(16),
elevation: CoreCardElevation.soft,
borderRadius: BorderRadius.circular(12),
body: Text('...'),
)
// After — cardStyle 묶음
CoCard(
cardStyle: CoreCardStyle(
sizing: CoreCardSizing.fixed,
width: 240,
padding: EdgeInsets.all(16),
elevation: CoreCardElevation.soft,
borderRadius: BorderRadius.circular(12),
),
body: Text('...'),
)
기존 외부 프로젝트 호환을 위해 마이그레이션 도우미는 제공하지 않으며, 각 사용처를 직접 새
API 로 옮겨주세요. CoCard 자신과 모든 co_*.dart 사용처는 sweep 완료된 상태입니다.