NavigationMenu#
호버 또는 클릭으로 콘텐츠 패널이 펼쳐지는 가로 메가-메뉴 컴포넌트입니다. 각 상위 항목은 콘텐츠 패널을 가지거나, 직접 링크이거나, 액션 콜백을 가질 수 있습니다.
NavigationMenu는 데이터 기반 컴포넌트입니다. CoreNavigationMenuItem
목록을 넘기면 각 항목이 가로 트리거 바에 라벨로 표시되고, content가 있는
항목은 호버/클릭 시 드롭다운 패널로 펼쳐집니다. Flutter와 Web이 동일한 파라미터
이름·동작을 갖습니다.
Live Preview#
class NavigationMenuDefaultExample extends StatelessComponent {
const NavigationMenuDefaultExample({super.key});
@override
Component build(BuildContext context) {
return NavigationMenu(
items: [
CoreNavigationMenuItem(
label: 'Products',
content: div(
[
Text('Web Apps').titleSmall.medium,
Text('Build modern web experiences').bodySmall.onSurfaceVariant,
Text('Mobile Apps').titleSmall.medium,
Text('Native iOS and Android apps').bodySmall.onSurfaceVariant,
],
styles: const Styles(
display: Display.flex,
flexDirection: FlexDirection.column,
gap: Gap.all(Unit.pixels(CoreSpace.space8)),
),
),
),
CoreNavigationMenuItem(
label: 'Solutions',
content: div(
[
Text('For Startups').titleSmall.medium,
Text('Scale from zero to launch').bodySmall.onSurfaceVariant,
Text('For Enterprise').titleSmall.medium,
Text('Security and compliance built in').bodySmall
.onSurfaceVariant,
],
styles: const Styles(
display: Display.flex,
flexDirection: FlexDirection.column,
gap: Gap.all(Unit.pixels(CoreSpace.space8)),
),
),
),
CoreNavigationMenuItem(label: 'Pricing', onTap: () {}),
CoreNavigationMenuItem(label: 'About', onTap: () {}),
],
);
}
}
class NavigationMenuDefaultExample extends StatelessWidget {
const NavigationMenuDefaultExample({super.key});
@override
Widget build(BuildContext context) {
return NavigationMenu(
items: [
CoreNavigationMenuItem(
label: 'Products',
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: CoreSpace.space8,
children: [
Text('Web Apps').titleSmall.medium,
Text('Build modern web experiences').bodySmall.onSurfaceVariant,
Text('Mobile Apps').titleSmall.medium,
Text('Native iOS and Android apps').bodySmall.onSurfaceVariant,
],
),
),
CoreNavigationMenuItem(
label: 'Solutions',
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
spacing: CoreSpace.space8,
children: [
Text('For Startups').titleSmall.medium,
Text('Scale from zero to launch').bodySmall.onSurfaceVariant,
Text('For Enterprise').titleSmall.medium,
Text('Security and compliance built in').bodySmall
.onSurfaceVariant,
],
),
),
CoreNavigationMenuItem(label: 'Pricing', onTap: () {}),
CoreNavigationMenuItem(label: 'About', onTap: () {}),
],
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 사이트 전역 네비게이션에서 카테고리별 풍부한 콘텐츠 패널이 필요할 때
- 마케팅 사이트의 헤더처럼 제품/솔루션을 메가-메뉴로 정리할 때
- 호버로 미리보기를 제공하면서 클릭으로도 동작해야 할 때
대신 다른 컴포넌트를 사용하세요:
Menubar: 데스크탑 앱 스타일의 파일/편집 메뉴 바에DropdownMenu: 단일 버튼의 드롭다운 액션 목록에Menu: 사이드바 형태의 일반 메뉴에
기본 사용법 (Basic Usage)#
NavigationMenu(
items: [
CoreNavigationMenuItem(
label: 'Products',
content: const Text('Browse the product catalog'),
),
CoreNavigationMenuItem(label: 'Pricing', href: '/pricing'),
CoreNavigationMenuItem(label: 'About', onTap: () {}),
],
)
NavigationMenu(
items: [
CoreNavigationMenuItem(
label: 'Products',
content: const Text('Browse the product catalog'),
),
CoreNavigationMenuItem(label: 'Pricing', href: '/pricing'),
CoreNavigationMenuItem(label: 'About', onTap: () {}),
],
)
Props / Parameters#
NavigationMenu#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
items |
List<CoreNavigationMenuItem> |
필수 | 가로 트리거 바에 표시되는 상위 항목 목록 |
activeIndex |
int? |
null |
활성 항목 인덱스. 지정 시 제어 모드, 생략 시 내부 상태 관리 |
onActiveIndexChanged |
void Function(int?)? |
null |
활성 항목이 바뀔 때 호출 (null이면 닫힘) |
variant |
CoreNavigationMenuVariant |
standard |
시각 변형 |
navigationMenuStyle |
CoreNavigationMenuStyle? |
null |
인스턴스별 chrome / 치수 스타일 |
CoreNavigationMenuItem#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
label | String | 필수 | 가로 트리거 바에 표시되는 라벨 |
content |
Widget? / Component? |
null |
펼쳐지는 드롭다운 콘텐츠 (콘텐츠 항목) |
href |
String? |
null |
직접 링크 URL (링크 항목) |
onTap |
CoreVoidCallback? |
null |
액션 항목 활성화 시 호출 |
스타일 시스템 (Style System)#
NavigationMenu의 모든 chrome / 치수 오버라이드는 단일
CoreNavigationMenuStyle 슬롯으로 흐릅니다 (Epic #1415 option D). 가로 트리거
바와 드롭다운 패널 chrome을 모두 한 클래스가 받습니다.
시맨틱 vs 스타일#
-
시맨틱 / behaviour: 위젯 파라미터로 직접 (
variant,items,activeIndex) -
chrome / dimensional:
navigationMenuStyle: CoreNavigationMenuStyle?한 곳으로
Resolve chain#
CoreNavigationMenuStyle.defaultX // 디자인 시스템 기본값
→ CoreNavigationMenuTheme.style // 프로젝트 공통
→ CoreNavigationMenuTheme.variantStyles[variant] // variant별 공통
→ widget.navigationMenuStyle // 인스턴스별
CoreNavigationMenuStyle 주요 필드#
| 필드 | 타입 | 적용 영역 |
|---|---|---|
triggerPadding | CoreEdgeInsets? | 트리거 내부 패딩 |
triggerBorderRadius | CoreBorderRadius? | 트리거 모서리 반경 |
triggerHoverColor |
CoreColor? |
트리거 hover / active 배경색 |
triggerSpacing | double? | 인접 트리거 사이 간격 |
panelPadding | CoreEdgeInsets? | 드롭다운 패널 내부 패딩 |
panelBorderRadius |
CoreBorderRadius? |
드롭다운 패널 모서리 반경 |
panelBorderWidth | double? | 드롭다운 패널 보더 두께 |
panelBackgroundColor | CoreColor? | 드롭다운 패널 배경색 |
panelBorderColor | CoreColor? | 드롭다운 패널 보더 색 |
panelMinWidth | double? | 드롭다운 패널 최소 너비 |
panelBoxShadow |
List<CoreShadowLayer>? |
드롭다운 패널 그림자 |
menuOffset | double? | 트리거 바와 패널 사이 간격 |
duration | Duration? | 열림 / 닫힘 색상 전환 시간 |
사용 예 (Flutter / Web 동일)#
NavigationMenu(
navigationMenuStyle: const CoreNavigationMenuStyle(
panelBorderRadius: CoreBorderRadius.all(CoreRadius.radius16),
panelBackgroundColor: CoreColor.token(CoreColors.surface),
),
items: [
CoreNavigationMenuItem(
label: 'Products',
content: const Text('Browse the product catalog'),
),
],
)
동작 스펙 (Behavior)#
인터랙션#
- 호버:
content를 가진 트리거에 호버하면 패널이 펼쳐짐 -
클릭:
content트리거 클릭 시 토글,href트리거는 링크 이동,onTap트리거는 콜백 호출 - 외부 클릭: 메뉴 외부 클릭 시 닫힘
- Escape: 열린 패널 닫힘
상태 전환#
closed→open: 콘텐츠 트리거 호버 / 클릭open→open (다른 항목): 다른 콘텐츠 트리거 호버 / 클릭open→closed: 외부 클릭, Escape, 패널/트리거 밖으로 마우스 이탈
애니메이션#
- 트리거 hover 배경: 150ms 색상 전환 (
CoreDuration.ms150) - chevron 회전: 활성 시 180° (
CoreDuration.ms150)
접근성 (Accessibility)#
시맨틱 역할#
- 가로 바:
role="menubar"+aria-orientation="horizontal" - 콘텐츠 트리거:
role="menuitem"+aria-haspopup+aria-expanded - 링크 트리거:
<a role="menuitem"> - 드롭다운 패널:
role="menu"
키보드 인터랙션#
| 키 | 동작 |
|---|---|
Escape | 열린 패널 닫기 |
스크린 리더#
- Flutter:
Semantics로 navigation / button / link / expanded 상태 전달 -
Web:
role="menubar"/role="menuitem"/role="menu"자동 적용
크로스 플랫폼 차이점 (Platform Differences)#
NavigationMenu는 Flutter와 Web이 동일한 items / activeIndex /
onActiveIndexChanged / variant / navigationMenuStyle
API와 동작을
갖습니다. 드롭다운 패널은 Flutter에서는 OverlayPortal, Web에서는 루트
OverlayHost의 menu 레이어로 portal되어 양쪽 모두 ancestor overflow에
잘리지 않습니다.
관련 컴포넌트 (Related Components)#
- Menubar: 데스크탑 앱 스타일의 메뉴 바
- Menu: 사이드바 형태의 일반 메뉴
- Navigation: 페이지 이동을 위한 네비게이션 바