Menu | CoUI

Menu

정적 메뉴 패널 컴포넌트

Menu#

Menu 는 테두리가 있는 패널 안에 풍부한 메뉴 항목 목록을 표시하는 정적(인라인) 메뉴 컴포넌트입니다. 항목은 CoreMenuItem 의 이름 있는 팩토리 생성자로 만듭니다 — 헤딩 라벨, 액션, 체크박스, 라디오, 구분선, 간격, 그리고 인라인 중첩 서브메뉴까지 지원합니다.

Live Preview#

Web
Flutter
Loading Flutter...
class MenuDefaultExample extends StatelessComponent {
  const MenuDefaultExample({super.key});

  @override
  Component build(BuildContext context) {
    return Menu(
      ariaLabel: 'Main menu',
      items: [
        const CoreMenuItem.label('Navigation'),
        CoreMenuItem.action('Home', onPressed: () {}, active: true),
        CoreMenuItem.action('Profile', onPressed: () {}),
        const CoreMenuItem.divider(),
        CoreMenuItem.checkbox('Show grid', onChanged: (_) {}, checked: true),
        CoreMenuItem.action('Settings', disabled: true),
      ],
    );
  }
}
class MenuDefaultExample extends StatelessWidget {
  const MenuDefaultExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Menu(
      ariaLabel: 'Main menu',
      items: [
        const CoreMenuItem.label('Navigation'),
        CoreMenuItem.action('Home', onPressed: () {}, active: true),
        CoreMenuItem.action('Profile', onPressed: () {}),
        const CoreMenuItem.divider(),
        CoreMenuItem.checkbox('Show grid', onChanged: (_) {}, checked: true),
        CoreMenuItem.action('Settings', disabled: true),
      ],
    );
  }
}

사용 시기 (When to Use)#

이 컴포넌트를 사용하세요:

  • 액션이나 네비게이션 항목 목록을 패널로 직접 표시할 때
  • leading/trailing 아이콘, 체크박스, 라디오를 포함한 메뉴가 필요할 때
  • 인라인으로 펼쳐지는 계층적 서브메뉴 구조가 필요할 때

대신 다른 컴포넌트를 사용하세요:

  • DropdownMenu: 트리거 클릭으로 떠오르는 오버레이 메뉴일 때
  • ContextMenu: 우클릭/롱프레스로 여는 컨텍스트 메뉴일 때
  • Menubar: 가로 애플리케이션 메뉴바일 때
  • Select: 폼에서 값을 선택하는 드롭다운일 때

기본 사용법 (Basic Usage)#

Menu(
  ariaLabel: 'Main menu',
  items: [
    const CoreMenuItem.label('Navigation'),
    CoreMenuItem.action('Home', onPressed: () {}, active: true),
    CoreMenuItem.action('Profile', onPressed: () {}),
    const CoreMenuItem.divider(),
    CoreMenuItem.checkbox('Show grid', onChanged: (_) {}, checked: true),
    CoreMenuItem.action('Settings', disabled: true),
  ],
)
Menu(
  ariaLabel: 'Main menu',
  items: [
    const CoreMenuItem.label('Navigation'),
    CoreMenuItem.action('Home', onPressed: () {}, active: true),
    CoreMenuItem.action('Profile', onPressed: () {}),
    const CoreMenuItem.divider(),
    CoreMenuItem.checkbox('Show grid', onChanged: (_) {}, checked: true),
    CoreMenuItem.action('Settings', disabled: true),
  ],
)

Props / Parameters#

속성타입기본값설명
items List<CoreMenuItem<W>> 필수 패널에 렌더링할 메뉴 항목
vertical bool true false 면 항목을 가로로 배치
ariaLabel String? null 메뉴 컨테이너의 접근성 이름
menuStyle CoreMenuStyle? null 인스턴스별 chrome 스타일

CoreMenuItem 팩토리#

팩토리설명
CoreMenuItem.label(text, {leading, trailing})비대화 섹션 헤딩
CoreMenuItem.action(text, {leading, trailing, onPressed, href, submenu, active, disabled}) 대화형 액션 항목 (submenu 지정 시 인라인 서브메뉴 트리거)
CoreMenuItem.checkbox(text, {trailing, onChanged, checked, disabled}) 체크박스 토글 항목
CoreMenuItem.radio(text, {trailing, onChanged, selected, disabled}) 라디오 단일 선택 항목
CoreMenuItem.divider()시각적 구분선
CoreMenuItem.gap(size)고정 크기 간격

스타일 시스템 (Style System)#

Menu 의 모든 chrome / dimensional 오버라이드는 단일 CoreMenuStyle 슬롯으로 흐릅니다.

시맨틱 vs 스타일#

  • 시맨틱 / behaviour: 위젯 파라미터로 직접 (vertical, ariaLabel)
  • chrome / dimensional: CoreMenuStyle 한 곳으로 (background / border / radius / padding / itemPadding / itemSpacing / itemContentGapStyle / activeColor / hoverColor / dividerColor / leadingColumnWidth / submenuIndent)

Resolve chain#

CoreMenuStyle.defaultX (디자인 시스템 기본값)
  → CoreMenuTheme.style                  // 프로젝트 공통
  → widget.menuStyle                     // 인스턴스별

사용 예#

Menu(
  items: items,
  menuStyle: const CoreMenuStyle(
    borderRadius: CoreBorderRadius.all(CoreRadius.radius12),
    activeColor: CoreColor.token(CoreColors.secondary),
  ),
)

변형 (Variants)#

서브메뉴 (인라인 중첩)#

Menu(
  items: [
    CoreMenuItem.action('File', onPressed: () {}),
    CoreMenuItem.action(
      'Export',
      submenu: [
        CoreMenuItem.action('PDF', onPressed: () {}),
        CoreMenuItem.action('CSV', onPressed: () {}),
      ],
    ),
  ],
)

서브메뉴는 부모 항목 아래에 한 단계 들여쓰기되어 인라인으로 펼쳐집니다.

가로 메뉴#

Menu(
  vertical: false,
  items: [
    CoreMenuItem.action('Home', onPressed: () {}),
    CoreMenuItem.action('Docs', onPressed: () {}),
  ],
)

체크 / 라디오 항목#

Menu(
  items: [
    CoreMenuItem.checkbox('Auto save', onChanged: (_) {}, checked: true),
    CoreMenuItem.radio('Compact', onChanged: (_) {}, selected: true),
    CoreMenuItem.radio('Comfortable', onChanged: (_) {}),
  ],
)

동작 스펙 (Behavior)#

항목 활성화#

  • action: 클릭/탭으로 onPressed 호출. disabled 면 무시 + 0.5 불투명도
  • checkbox: 활성화 시 onChanged(!checked) 호출, 체크 시 leading 체크 아이콘 표시
  • radio: 활성화 시 onChanged(true) 호출, 선택 시 leading dot 아이콘 표시

Hover#

  • 대화형 항목에 포인터를 올리면 hoverColor 배경이 표시됩니다.

서브메뉴#

  • CoreMenuItem.actionsubmenu 를 지정하면 trailing 위치에 chevron 아이콘이 자동 추가되고, 항목 아래에 중첩 Menu 가 인라인으로 렌더됩니다.

접근성 (Accessibility)#

스크린 리더#

  • Flutter: ariaLabelSemantics 컨테이너 라벨로 노출됩니다.
  • Web: role="menu" (세로) / role="menubar" (가로), 항목별 role="menuitem" / menuitemcheckbox / menuitemradio, aria-orientation, aria-disabled, aria-current, aria-checked 로 상태를 전달합니다.

크로스 플랫폼 차이점 (Platform Differences)#

항목FlutterWeb
패널 Container + Column/Row <div> flex 패널
항목 CoreMenuItem 팩토리 (양쪽 동일 API) CoreMenuItem 팩토리 (양쪽 동일 API)
구분선Separator<hr> (border 기반)
서브메뉴인라인 중첩 Menu인라인 중첩 Menu
아이콘IconIcon
ARIA Flutter Semantics role / aria-* 속성
  • DropdownMenu: 트리거로 떠오르는 오버레이 메뉴
  • ContextMenu: 우클릭/롱프레스 컨텍스트 메뉴
  • Menubar: 가로 애플리케이션 메뉴바
  • Popover: 임의의 콘텐츠를 표시하는 팝업