Alert#
다양한 상태(정보, 성공, 경고, 오류)를 시각적으로 구분하여 메시지를 표시하는 알림 영역 컴포넌트입니다. Flutter와 Web이 동일한 named properties API(title,
description, variant, onDismiss, action)를 사용합니다.
Live Preview#
class AlertDefaultExample extends StatefulComponent {
const AlertDefaultExample({super.key});
@override
State<AlertDefaultExample> createState() => _AlertDefaultExampleState();
}
class _AlertDefaultExampleState extends State<AlertDefaultExample> {
bool _show = true;
@override
Component build(BuildContext context) {
if (_show) {
return CoAlert(
title: 'Heads up!',
description: 'You can add components to your app using the cli.',
onDismiss: () => setState(() => _show = false),
);
}
return CoButton(
variant: CoreButtonVariant.outline,
onPressed: () => setState(() => _show = true),
child: const Text('Show alert'),
);
}
}
class AlertDefaultExample extends StatefulWidget {
const AlertDefaultExample({super.key});
@override
State<AlertDefaultExample> createState() => _AlertDefaultExampleState();
}
class _AlertDefaultExampleState extends State<AlertDefaultExample> {
bool _show = true;
@override
Widget build(BuildContext context) {
if (_show) {
return CoAlert(
title: 'Heads up!',
description: 'You can add components to your app using the cli.',
onDismiss: () => setState(() => _show = false),
);
}
return CoButton(
variant: CoreButtonVariant.outline,
onPressed: () => setState(() => _show = true),
child: const Text('Show alert'),
);
}
}
class AlertInfoExample extends StatefulComponent {
const AlertInfoExample({super.key});
@override
State<AlertInfoExample> createState() => _AlertInfoExampleState();
}
class _AlertInfoExampleState extends State<AlertInfoExample> {
bool _show = true;
@override
Component build(BuildContext context) {
if (_show) {
return CoAlert.info(
title: '정보',
description: '새로운 업데이트가 있습니다.',
onDismiss: () => setState(() => _show = false),
);
}
return CoButton(
variant: CoreButtonVariant.outline,
onPressed: () => setState(() => _show = true),
child: const Text('Show alert'),
);
}
}
class AlertInfoExample extends StatefulWidget {
const AlertInfoExample({super.key});
@override
State<AlertInfoExample> createState() => _AlertInfoExampleState();
}
class _AlertInfoExampleState extends State<AlertInfoExample> {
bool _show = true;
@override
Widget build(BuildContext context) {
if (_show) {
return CoAlert.info(
title: '정보',
description: '새로운 업데이트가 있습니다.',
onDismiss: () => setState(() => _show = false),
);
}
return CoButton(
variant: CoreButtonVariant.outline,
onPressed: () => setState(() => _show = true),
child: const Text('Show alert'),
);
}
}
class AlertSuccessExample extends StatefulComponent {
const AlertSuccessExample({super.key});
@override
State<AlertSuccessExample> createState() => _AlertSuccessExampleState();
}
class _AlertSuccessExampleState extends State<AlertSuccessExample> {
bool _show = true;
@override
Component build(BuildContext context) {
if (_show) {
return CoAlert.success(
title: '저장 완료',
description: '변경 사항이 성공적으로 저장되었습니다.',
onDismiss: () => setState(() => _show = false),
);
}
return CoButton(
variant: CoreButtonVariant.outline,
onPressed: () => setState(() => _show = true),
child: const Text('Show alert'),
);
}
}
class AlertSuccessExample extends StatefulWidget {
const AlertSuccessExample({super.key});
@override
State<AlertSuccessExample> createState() => _AlertSuccessExampleState();
}
class _AlertSuccessExampleState extends State<AlertSuccessExample> {
bool _show = true;
@override
Widget build(BuildContext context) {
if (_show) {
return CoAlert.success(
title: '저장 완료',
description: '변경 사항이 성공적으로 저장되었습니다.',
onDismiss: () => setState(() => _show = false),
);
}
return CoButton(
variant: CoreButtonVariant.outline,
onPressed: () => setState(() => _show = true),
child: const Text('Show alert'),
);
}
}
class AlertWarningExample extends StatefulComponent {
const AlertWarningExample({super.key});
@override
State<AlertWarningExample> createState() => _AlertWarningExampleState();
}
class _AlertWarningExampleState extends State<AlertWarningExample> {
bool _show = true;
@override
Component build(BuildContext context) {
if (_show) {
return CoAlert.warning(
title: '주의',
description: '이 작업은 되돌릴 수 없습니다.',
onDismiss: () => setState(() => _show = false),
);
}
return CoButton(
variant: CoreButtonVariant.outline,
onPressed: () => setState(() => _show = true),
child: const Text('Show alert'),
);
}
}
class AlertWarningExample extends StatefulWidget {
const AlertWarningExample({super.key});
@override
State<AlertWarningExample> createState() => _AlertWarningExampleState();
}
class _AlertWarningExampleState extends State<AlertWarningExample> {
bool _show = true;
@override
Widget build(BuildContext context) {
if (_show) {
return CoAlert.warning(
title: '주의',
description: '이 작업은 되돌릴 수 없습니다.',
onDismiss: () => setState(() => _show = false),
);
}
return CoButton(
variant: CoreButtonVariant.outline,
onPressed: () => setState(() => _show = true),
child: const Text('Show alert'),
);
}
}
class AlertDestructiveExample extends StatefulComponent {
const AlertDestructiveExample({super.key});
@override
State<AlertDestructiveExample> createState() =>
_AlertDestructiveExampleState();
}
class _AlertDestructiveExampleState extends State<AlertDestructiveExample> {
bool _show = true;
@override
Component build(BuildContext context) {
if (_show) {
return CoAlert.destructive(
title: '오류 발생',
description: '요청을 처리하는 중 문제가 발생했습니다.',
onDismiss: () => setState(() => _show = false),
);
}
return CoButton(
variant: CoreButtonVariant.outline,
onPressed: () => setState(() => _show = true),
child: const Text('Show alert'),
);
}
}
class AlertDestructiveExample extends StatefulWidget {
const AlertDestructiveExample({super.key});
@override
State<AlertDestructiveExample> createState() =>
_AlertDestructiveExampleState();
}
class _AlertDestructiveExampleState extends State<AlertDestructiveExample> {
bool _show = true;
@override
Widget build(BuildContext context) {
if (_show) {
return CoAlert.destructive(
title: '오류 발생',
description: '요청을 처리하는 중 문제가 발생했습니다.',
onDismiss: () => setState(() => _show = false),
);
}
return CoButton(
variant: CoreButtonVariant.outline,
onPressed: () => setState(() => _show = true),
child: const Text('Show alert'),
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 페이지 내 고정된 위치에서 중요한 상태 메시지를 안내할 때
- 폼 제출 결과(성공/오류)를 인라인으로 표시할 때
- 사용자가 반드시 인지해야 하는 경고나 안내를 표시할 때
대신 다른 컴포넌트를 사용하세요:
Toast: 일시적으로 나타났다 사라지는 짧은 알림에는 Toast 사용Banner: 페이지 최상단 전체 폭으로 중요 공지를 표시할 때 사용Dialog: 사용자의 확인 액션이 필요한 경고성 메시지에는 Dialog 사용
기본 사용법 (Basic Usage)#
// 기본 알림
CoAlert(
title: 'Heads up!',
description: 'You can add components to your app using the cli.',
)
// 정보 알림 (variant named constructor)
CoAlert.info(
title: '안내',
description: '새로운 업데이트가 있습니다.',
)
// 성공 알림 (닫기 버튼 포함)
CoAlert.success(
title: '저장 완료',
description: '변경 사항이 성공적으로 저장되었습니다.',
onDismiss: handleDismiss,
)
// 오류 알림
CoAlert.destructive(
title: '오류 발생',
description: '요청을 처리하는 중 문제가 발생했습니다.',
)
// 기본 알림
CoAlert(
title: 'Heads up!',
description: 'You can add components to your app using the cli.',
)
// 정보 알림
CoAlert.info(
title: '안내',
description: '새로운 업데이트가 있습니다.',
)
// 성공 알림 (닫기 버튼 포함)
CoAlert.success(
title: '저장 완료',
description: '변경 사항이 성공적으로 저장되었습니다.',
onDismiss: handleDismiss,
)
// 오류 알림
CoAlert.destructive(
title: '오류 발생',
description: '요청을 처리하는 중 문제가 발생했습니다.',
)
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
description | String | 필수 | 알림 본문 메시지 |
title | String? | null | 알림 제목 |
variant |
CoreAlertVariant |
defaultVariant |
알림 종류 (
defaultVariant
/
info
/
success
/
warning
/
destructive
)
|
onDismiss |
VoidCallback? (Flutter) / CoreVoidCallback? (Web) |
null |
닫기 버튼 클릭 핸들러. null이면 닫기 버튼 미표시 |
action |
Widget? (Flutter) / Component? (Web) |
null |
알림 우측에 표시할 액션 위젯 |
alertStyle |
CoreAlertStyle<Color>? / CoreAlertStyle<CoreColor>? |
null |
인스턴스 스타일 (Style 시스템 참조) |
스타일 시스템 (Style System)#
Alert 의 모든 chrome / dimensional / nested-slot 오버라이드는 CoreAlertStyle<Clr> 단일 슬롯으로 흐릅니다 (Epic #1302 원칙 6/7/8). 시맨틱 enum (variant) 과 behaviour (title
/ description / action / onDismiss) 는 위젯 파라미터로 직접 전달합니다.
시맨틱 vs 스타일#
-
시맨틱 enum / behaviour: 위젯/컴포넌트 파라미터로 직접 (
variant,title,description,action,onDismiss) -
chrome / dimensional / 슬롯 스타일:
CoreAlertStyle한 곳으로 (backgroundColor/foregroundColor/borderColor/borderWidth/borderRadius/paddingH/paddingV/gap/titleStyle/descriptionStyle/iconStyle/actionButtonStyle/closeButtonStyle)
Resolve chain#
design system default for alert
→ CoreAlertTheme.style // 프로젝트 공통
→ parent component slot override
→ widget.alertStyle // 인스턴스별
각 nested 슬롯 스타일 (titleStyle / descriptionStyle / iconStyle / actionButtonStyle
/ closeButtonStyle) 은 자기 컴포넌트의 자체 resolve chain 으로 다시 한 번 머지됩니다.
슬롯 매핑 (CoreAlertStyle 13 필드)#
| 필드 | 타입 (Flutter) | 적용 영역 |
|---|---|---|
backgroundColor |
Color? |
알림 배경 색 (variant 기본값 위에 오버라이드) |
foregroundColor | Color? | 제목 / 설명 전경 색 |
borderColor | Color? | 보더 색 |
borderWidth | double? | 보더 두께 (px) |
borderRadius | double? | 모서리 반지름 (px) |
paddingH | double? | 좌/우 패딩 (px) |
paddingV | double? | 위/아래 패딩 (px) |
gap | double? | 아이콘 / 콘텐츠 / 액션 / 닫기 간격 (px) |
titleStyle | CoreTextStyle<Color>? | 제목 텍스트 스타일 |
descriptionStyle |
CoreTextStyle<Color>? |
설명 텍스트 스타일 |
iconStyle |
CoreIconStyle<Color>? |
variant 아이콘 스타일 |
actionButtonStyle |
CoreButtonStyle<Color>? |
액션 버튼 chrome (호스트가 CoButton 을 주입한 경우 전달) |
closeButtonStyle |
CoreButtonStyle<Color>? |
닫기 버튼 chrome (현재
foregroundColor
/
labelStyle.color
만 적용; 완전
CoButton
wrap 은 #1322 이월)
|
Migration — 옛 평면 chrome → 새 위치 매핑#
기존 CoreAlertTheme 의 평면 chrome (backgroundColor / borderColor /
borderRadius) 은 호환을 위해 유지되지만, 새 코드는 alertStyle 슬롯을 사용하세요:
| 기존 (legacy theme 필드) | 새 위치 |
|---|---|
CoreAlertTheme.backgroundColor |
alertStyle.backgroundColor 또는 CoreAlertTheme.style.backgroundColor |
CoreAlertTheme.borderColor |
alertStyle.borderColor 또는 CoreAlertTheme.style.borderColor |
CoreAlertTheme.borderRadius |
alertStyle.borderRadius 또는 CoreAlertTheme.style.borderRadius |
| 인스턴스 padding 오버라이드 (불가능) | alertStyle.paddingH / paddingV |
| 인스턴스 제목 텍스트 스타일 (불가능) | alertStyle.titleStyle |
| 인스턴스 설명 텍스트 스타일 (불가능) | alertStyle.descriptionStyle |
| 인스턴스 아이콘 사이즈/색 (불가능) | alertStyle.iconStyle |
사용 예 (Flutter)#
CoAlert.success(
title: '저장 완료',
description: '변경 사항이 성공적으로 저장되었습니다.',
alertStyle: CoreAlertStyle(
paddingH: 24,
paddingV: 16,
borderRadius: 12,
titleStyle: CoreTextStyle(fontSize: 14, fontWeight: 600),
descriptionStyle: CoreTextStyle(fontSize: 12),
iconStyle: CoreIconStyle(size: 20),
),
onDismiss: handleDismiss,
)
사용 예 (Web)#
CoAlert.success(
title: '저장 완료',
description: '변경 사항이 성공적으로 저장되었습니다.',
alertStyle: CoreAlertStyle<CoreColor>(
paddingH: 24,
paddingV: 16,
borderRadius: 12,
),
onDismiss: handleDismiss,
)
Named constructors#
-
CoAlert.info(...),CoAlert.success(...),CoAlert.warning(...),CoAlert.destructive(...)— 각 variant에 대한 편의 생성자.
변형 (Variants)#
Default#
CoAlert(
title: 'Heads up!',
description: '표준 surface 색상을 사용하는 기본 알림입니다.',
)
Info#
CoAlert.info(
title: '정보',
description: '시스템 점검이 예정되어 있습니다.',
)
Success#
CoAlert.success(
title: '완료',
description: '파일이 성공적으로 업로드되었습니다.',
onDismiss: handleDismiss,
)
Warning#
CoAlert.warning(
title: '주의',
description: '이 작업은 되돌릴 수 없습니다.',
)
Destructive (Error)#
CoAlert.destructive(
title: '오류',
description: '네트워크 연결을 확인해 주세요.',
)
동작 스펙 (Behavior)#
인터랙션#
- 닫기:
onDismiss가 제공되면 우측에 닫기 버튼 표시, 클릭 시 콜백 실행. 실제 숨김 처리는 부모 위젯이 담당. - 액션:
action은 닫기 버튼 왼쪽에 표시. 버튼 등 임의 위젯을 전달할 수 있습니다. - 아이콘: variant별 아이콘(info/success/warning/destructive)이 자동 표시됩니다.
토큰#
- Border radius:
CoreRadius.box(16px) - Border width:
CoreBorderWidth.thin(1px) - Padding:
CoreSpace.space12세로 ×CoreSpace.space16가로 - Icon size:
CoreSpace.space16(16px) - Icon gap:
CoreSpace.space12(12px)
사용 가이드라인 (Usage Guidelines)#
✅ Do#
메시지의 심각도에 맞는 variant 사용
CoAlert.warning(
title: '주의',
description: '이 작업은 되돌릴 수 없습니다.',
)
색상과 아이콘이 심각도를 즉시 전달하여 사용자가 빠르게 인식합니다.
❌ Don't#
모든 안내 메시지에 destructive variant 사용 금지
// ❌ 일반 안내에 destructive 사용
CoAlert.destructive(
description: '새 버전이 출시되었습니다.',
)
잘못된 variant는 사용자에게 불필요한 긴장감을 주고 신뢰를 낮춥니다.
✅ Do#
일시적 피드백은 onDismiss로 처리
CoAlert.success(
title: '저장 완료',
description: '변경 사항이 저장되었습니다.',
onDismiss: handleDismiss,
)
사용자가 확인 후 직접 닫을 수 있어 화면 공간을 효율적으로 사용합니다.
❌ Don't#
긴 설명 텍스트를 description에만 모두 넣지 않기
// ❌ 너무 긴 description
CoAlert.warning(
description: '시스템 점검으로 인해 2024년 3월 15일 오전 2시부터 4시까지 서비스가 일시 중단됩니다. 이용에 불편을 드려 죄송합니다.',
)
title로 핵심 요약, description으로 보충 설명을 분리해야 스캔하기 쉽습니다.
접근성 (Accessibility)#
스크린 리더#
- Flutter:
Semantics(container: true, label: title ?? 'Alert')자동 적용. - Web:
role="alert"속성이 루트에 자동 부여됩니다.
키보드 인터랙션#
| 키 | 동작 |
|---|---|
Tab | 닫기 버튼 / action 위젯으로 포커스 이동 |
Enter / Space | 포커스된 버튼 활성화 |
크로스 플랫폼 차이점 (Platform Differences)#
v3.0부터
title,description,variant,onDismiss,actionAPI가 통일되었습니다. 아래는 플랫폼 고유 차이점만 나열합니다.
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | CoAlert | CoAlert |
| action 타입 | Widget? | Component? |
| 콜백 타입 | VoidCallback? | CoreVoidCallback? |
| 아이콘 | Icon (Icons.info_outline 등) | 인라인 SVG |
관련 컴포넌트 (Related Components)#
조합 예제#
// 폼 제출 후 결과 표시
Column(
children: [
if (submitError != null)
CoAlert.destructive(
title: '오류',
description: submitError!,
onDismiss: handleErrorDismissed,
),
// ... form fields
],
)