Menubar#
데스크탑 애플리케이션 스타일의 상단 가로 메뉴 바 컴포넌트입니다. 각 상위 메뉴는 클릭 시 드롭다운 패널을 열고, 키보드 단축키 힌트와 구분선을 지원합니다.
Menubar는 데이터 기반 컴포넌트입니다. CoreMenubarMenu 목록을 넘기면 각
메뉴가 가로 바에 라벨로 표시되고, entries의 CoreMenubarEntry.action
/
CoreMenubarEntry.divider가 드롭다운 패널로 렌더링됩니다. Flutter와 Web이
동일한 파라미터 이름·동작을 갖습니다.
Live Preview#
class MenubarDefaultExample extends StatelessComponent {
const MenubarDefaultExample({super.key});
@override
Component build(BuildContext context) {
return Menubar(
menus: const [
CoreMenubarMenu(
label: 'File',
entries: [
CoreMenubarEntry.action(label: 'New', shortcut: 'Ctrl+N'),
CoreMenubarEntry.action(label: 'Open', shortcut: 'Ctrl+O'),
CoreMenubarEntry.divider(),
CoreMenubarEntry.action(label: 'Exit'),
],
),
CoreMenubarMenu(
label: 'Edit',
entries: [
CoreMenubarEntry.action(label: 'Cut', shortcut: 'Ctrl+X'),
CoreMenubarEntry.action(label: 'Copy', shortcut: 'Ctrl+C'),
CoreMenubarEntry.action(label: 'Paste', shortcut: 'Ctrl+V'),
],
),
CoreMenubarMenu(
label: 'View',
entries: [
CoreMenubarEntry.action(label: 'Zoom In'),
CoreMenubarEntry.action(label: 'Zoom Out'),
CoreMenubarEntry.divider(),
CoreMenubarEntry.action(label: 'Full Screen'),
],
),
],
);
}
}
class MenubarDefaultExample extends StatelessWidget {
const MenubarDefaultExample({super.key});
@override
Widget build(BuildContext context) {
return Menubar(
menus: const [
CoreMenubarMenu(
label: 'File',
entries: [
CoreMenubarEntry.action(label: 'New', shortcut: 'Ctrl+N'),
CoreMenubarEntry.action(label: 'Open', shortcut: 'Ctrl+O'),
CoreMenubarEntry.divider(),
CoreMenubarEntry.action(label: 'Exit'),
],
),
CoreMenubarMenu(
label: 'Edit',
entries: [
CoreMenubarEntry.action(label: 'Cut', shortcut: 'Ctrl+X'),
CoreMenubarEntry.action(label: 'Copy', shortcut: 'Ctrl+C'),
CoreMenubarEntry.action(label: 'Paste', shortcut: 'Ctrl+V'),
],
),
CoreMenubarMenu(
label: 'View',
entries: [
CoreMenubarEntry.action(label: 'Zoom In'),
CoreMenubarEntry.action(label: 'Zoom Out'),
CoreMenubarEntry.divider(),
CoreMenubarEntry.action(label: 'Full Screen'),
],
),
],
);
}
}
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- IDE, 문서 편집기, 데스크탑 앱처럼 파일/편집/보기 스타일의 메뉴 바가 필요할 때
- 많은 기능을 계층적 메뉴 구조로 정리해야 할 때
- 키보드 단축키와 함께 메뉴 항목을 표시할 때
대신 다른 컴포넌트를 사용하세요:
Navigation: 페이지 간 이동을 위한 네비게이션 메뉴에DropdownMenu: 단일 버튼의 드롭다운 액션 목록에Tabs: 동일 화면 내 콘텐츠 섹션 전환에
기본 사용법 (Basic Usage)#
Menubar(
menus: const [
CoreMenubarMenu(
label: 'File',
entries: [
CoreMenubarEntry.action(label: 'New', shortcut: 'Ctrl+N'),
CoreMenubarEntry.action(label: 'Open', shortcut: 'Ctrl+O'),
CoreMenubarEntry.divider(),
CoreMenubarEntry.action(label: 'Exit'),
],
),
CoreMenubarMenu(
label: 'Edit',
entries: [
CoreMenubarEntry.action(label: 'Cut', shortcut: 'Ctrl+X'),
CoreMenubarEntry.action(label: 'Copy', shortcut: 'Ctrl+C'),
CoreMenubarEntry.action(label: 'Paste', shortcut: 'Ctrl+V'),
],
),
],
)
Menubar(
menus: const [
CoreMenubarMenu(
label: 'File',
entries: [
CoreMenubarEntry.action(label: 'New', shortcut: 'Ctrl+N'),
CoreMenubarEntry.action(label: 'Open', shortcut: 'Ctrl+O'),
CoreMenubarEntry.divider(),
CoreMenubarEntry.action(label: 'Exit'),
],
),
CoreMenubarMenu(
label: 'Edit',
entries: [
CoreMenubarEntry.action(label: 'Cut', shortcut: 'Ctrl+X'),
CoreMenubarEntry.action(label: 'Copy', shortcut: 'Ctrl+C'),
CoreMenubarEntry.action(label: 'Paste', shortcut: 'Ctrl+V'),
],
),
],
)
Props / Parameters#
Menubar#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
menus |
List<CoreMenubarMenu> |
필수 | 가로 바에 표시되는 상위 메뉴 목록 |
border |
bool |
true |
가로 바에 외곽선 컨테이너를 그릴지 여부 |
variant |
CoreMenubarVariant |
standard |
시각 변형 |
menubarStyle |
CoreMenubarStyle? |
null |
인스턴스별 chrome / 치수 스타일 |
CoreMenubarMenu#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
label | String | 필수 | 가로 바에 표시되는 메뉴 라벨 |
entries |
List<CoreMenubarEntry> |
const [] |
메뉴를 열면 표시되는 드롭다운 엔트리 |
CoreMenubarEntry#
| 생성자 | 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|---|
.action |
label |
String |
필수 | 액션 행 텍스트 |
.action |
shortcut |
String? |
null |
라벨 뒤에 표시되는 단축키 힌트 |
.action |
enabled |
bool |
true |
비활성 시 muted 처리 + onSelect 미호출 |
.action |
onSelect |
CoreVoidCallback? |
null |
활성 액션 선택 시 호출 |
.divider | — | — | — | 그룹 사이 구분선 |
스타일 시스템 (Style System)#
Menubar의 모든 chrome / 치수 오버라이드는 단일 CoreMenubarStyle 슬롯으로
흐릅니다 (Epic #1415 option D). 가로 바와 드롭다운 패널 chrome을 모두 한 클래스가
받습니다.
시맨틱 vs 스타일#
-
시맨틱 / behaviour: 위젯 파라미터로 직접 (
border,variant,menus) - chrome / dimensional:
menubarStyle: CoreMenubarStyle?한 곳으로
Resolve chain#
CoreMenubarStyle.defaultX // 디자인 시스템 기본값
→ CoreMenubarTheme.style // 프로젝트 공통
→ CoreMenubarTheme.variantStyles[variant] // variant별 공통
→ widget.menubarStyle // 인스턴스별
CoreMenubarStyle 주요 필드#
| 필드 | 타입 | 적용 영역 |
|---|---|---|
barPadding | CoreEdgeInsets? | 가로 바 내부 패딩 |
barBorderRadius | CoreBorderRadius? | 가로 바 모서리 반경 |
barBorderWidth | double? | 가로 바 외곽선 두께 |
barBackgroundColor | CoreColor? | 가로 바 배경색 |
barBorderColor | CoreColor? | 가로 바 외곽선 색 |
triggerButtonStyle |
CoreButtonStyle? |
트리거 버튼 chrome (패딩 / 반경 — 트리거는
Button
합성, hover·open 색은
.menubar
variant 소유)
|
entryButtonStyle |
CoreButtonStyle? |
드롭다운 엔트리 버튼 chrome (엔트리도 Button 합성, hover·disabled 색은 .menu variant 소유) |
menuPanelStyle |
CorePopupStyle? |
드롭다운 패널 박스 chrome (패널은 Popup 합성 — 배경 / 반경 / 패딩 / 그림자 / 최소너비) |
separatorStyle |
CoreSeparatorStyle? |
구분선 엔트리 스타일 (색 / 두께 — Separator 합성) |
shortcutLeftMargin | double? | 엔트리 단축키 힌트 좌측 여백 |
shortcutTextStyle |
CoreTextStyle? |
엔트리 단축키 힌트 타이포그래피 |
separatorPadding | CoreEdgeInsets? | 구분선 래퍼 상하 패딩 |
labelTextStyle | CoreTextStyle? | 트리거 라벨 타이포그래피 |
menuOffset | double? | 바와 드롭다운 패널 사이 간격 |
duration | Duration? | 열림 / 닫힘 색상 전환 시간 |
사용 예 (Flutter / Web 동일)#
Menubar(
menubarStyle: const CoreMenubarStyle(
// 패널 박스는 Popup 합성 — chrome 은 menuPanelStyle 단일 슬롯으로.
menuPanelStyle: CorePopupStyle(
backgroundColor: CoreColor.token(CoreColors.surface),
minWidth: CoreSpace.space160,
),
// 트리거는 Button 합성 — padding / 반경은 triggerButtonStyle 로 흐른다.
triggerButtonStyle: CoreButtonStyle(
padding: CoreEdgeInsets.symmetric(
horizontal: CoreSpace.space16,
vertical: CoreSpace.space6,
),
),
),
menus: const [
CoreMenubarMenu(
label: 'File',
entries: [CoreMenubarEntry.action(label: 'New')],
),
],
)
변형 (Variants)#
단축키 포함#
키보드 단축키를 함께 표시합니다.
Menubar(
menus: const [
CoreMenubarMenu(
label: 'File',
entries: [
CoreMenubarEntry.action(label: 'New', shortcut: 'Ctrl+N'),
CoreMenubarEntry.action(label: 'Open', shortcut: 'Ctrl+O'),
CoreMenubarEntry.action(label: 'Save', shortcut: 'Ctrl+S'),
],
),
],
)
비활성 엔트리#
enabled: false 엔트리는 muted 처리되며 선택해도 onSelect가 호출되지 않습니다.
Menubar(
menus: const [
CoreMenubarMenu(
label: 'File',
entries: [
CoreMenubarEntry.action(label: 'New'),
CoreMenubarEntry.action(label: 'Exit', enabled: false),
],
),
],
)
동작 스펙 (Behavior)#
인터랙션#
- 클릭: 메뉴 바 항목 클릭 시 드롭다운 열기. 같은 항목 재클릭 시 닫힘
- 항목 선택: 활성 엔트리 클릭 시
onSelect호출 후 닫힘 - 외부 클릭: 메뉴 외부 클릭 시 닫힘
- Escape: 열린 드롭다운 닫힘
상태 전환#
closed→open: 메뉴 바 항목 클릭open→open (다른 메뉴): 다른 메뉴 바 항목 클릭open→closed: 항목 선택, 외부 클릭, Escape
애니메이션#
- 트리거 / 엔트리 hover 배경: 150ms 색상 전환 (
CoreDuration.ms150)
사용 가이드라인 (Usage Guidelines)#
✅ Do#
메뉴 항목을 논리적 그룹으로 구분선으로 분리
CoreMenubarMenu(
label: 'File',
entries: [
CoreMenubarEntry.action(label: 'New', shortcut: 'Ctrl+N'),
CoreMenubarEntry.action(label: 'Open', shortcut: 'Ctrl+O'),
CoreMenubarEntry.divider(),
CoreMenubarEntry.action(label: 'Save', shortcut: 'Ctrl+S'),
CoreMenubarEntry.divider(),
CoreMenubarEntry.action(label: 'Exit'),
],
)
구분선으로 관련 항목을 그룹화하면 메뉴 탐색이 더 쉽고 직관적이다.
❌ Don't#
하나의 메뉴에 너무 많은 항목 사용 (10개 초과)
// ❌ 너무 많은 항목
CoreMenubarMenu(
label: 'Tools',
entries: List.generate(
15,
(i) => CoreMenubarEntry.action(label: 'Tool ${i + 1}'),
),
)
항목이 너무 많으면 화면을 넘치거나 탐색이 어려워진다. 항목을 줄이거나 메뉴를 나눈다.
✅ Do#
자주 사용하는 항목에 키보드 단축키 표시
CoreMenubarEntry.action(label: 'Save', shortcut: 'Ctrl+S')
CoreMenubarEntry.action(label: 'Undo', shortcut: 'Ctrl+Z')
CoreMenubarEntry.action(label: 'Copy', shortcut: 'Ctrl+C')
단축키 표시가 파워 유저의 생산성을 높이고 메뉴를 통해 단축키를 학습하게 한다.
❌ Don't#
모바일 앱의 주요 네비게이션으로 Menubar 사용
// ❌ 모바일에 데스크탑 스타일 Menubar 강요
Menubar(
menus: const [
CoreMenubarMenu(label: 'Home'),
CoreMenubarMenu(label: 'Search'),
CoreMenubarMenu(label: 'Profile'),
],
)
Menubar는 데스크탑 환경에 최적화되어 있다. 모바일에서는 NavigationBar나
Drawer가 적합하다.
접근성 (Accessibility)#
시맨틱 역할#
- 가로 바:
role="menubar"+aria-orientation="horizontal" - 상위 메뉴 트리거:
role="menuitem"+aria-haspopup+aria-expanded - 드롭다운 패널:
role="menu" - 드롭다운 엔트리:
role="menuitem"(비활성 시aria-disabled) - 구분선:
role="separator"
키보드 인터랙션#
| 키 | 동작 |
|---|---|
Escape | 열린 드롭다운 닫기 |
스크린 리더#
- Flutter:
Semantics로 menubar / button / expanded 상태 전달 -
Web:
role="menubar"/role="menu"/role="menuitem"/role="separator"자동 적용
크로스 플랫폼 차이점 (Platform Differences)#
Menubar는 Flutter와 Web이 동일한 menus / border / variant
/
menubarStyle API와 동작을 갖습니다. 드롭다운 패널은 Flutter에서는
OverlayPortal, Web에서는 루트 OverlayHost의 menu 레이어로 portal되어
양쪽 모두 ancestor overflow에 잘리지 않습니다.
관련 컴포넌트 (Related Components)#
- Menu: 사이드바 형태의 일반 메뉴
- ContextMenu: 우클릭으로 표시되는 상황별 메뉴
- Navigation: 페이지 이동을 위한 네비게이션 메뉴