Navigation | CoUI

Navigation

네비게이션 바 컴포넌트

Navigation#

앱의 주요 탐색을 위한 네비게이션 바 컴포넌트입니다.

Live Preview#

Web
Flutter
Loading Flutter...
NavigationMenu([
  MenuItem([text('Dashboard')], isActive: true),
  MenuItem([text('Projects')]),
  MenuItem([text('Settings')]),
])
NavigationMenu(
  children: [
    NavigationMenuItem(
      onPressed: () {},
      child: Text('Dashboard'),
    ),
    NavigationMenuItem(
      onPressed: () {},
      child: Text('Projects'),
    ),
    NavigationMenuItem(
      onPressed: () {},
      child: Text('Settings'),
    ),
  ],
)

사용 시기 (When to Use)#

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

  • 앱의 주요 섹션 간 이동 수단이 필요할 때
  • 하단 네비게이션(모바일), 사이드바(데스크톱), 또는 상단 바가 필요할 때
  • 아이콘 + 라벨로 구성된 주요 메뉴를 제공할 때

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

  • Tabs: 같은 페이지 내 콘텐츠 전환일 때
  • Menu: 액션 메뉴가 필요할 때
  • Breadcrumb: 계층적 경로 표시일 때

기본 사용법 (Basic Usage)#

// 상단 네비게이션 바
NavBar(
  title: Text('앱 제목'),
  leading: Button.ghost(
    onPressed: handleBack,
    child: Icon(Icons.arrow_back),
  ),
  actions: [
    Button.ghost(
      onPressed: handleSearch,
      child: Icon(Icons.search),
    ),
  ],
)

// 하단 네비게이션
BottomNav(
  currentIndex: selectedIndex,
  onTap: handleNavTap,
  items: [
    BottomNavItem(icon: Icons.home, label: '홈'),
    BottomNavItem(icon: Icons.search, label: '검색'),
    BottomNavItem(icon: Icons.person, label: '프로필'),
  ],
)
NavBar(
  title: Text('앱 제목'),
  leading: Button.ghost(onClick: handleBack, child: Icon(Icons.arrow_back)),
  actions: [
    Button.ghost(onClick: handleSearch, child: Icon(Icons.search)),
  ],
)

BottomNav(
  currentIndex: selectedIndex,
  onTap: handleNavTap,
  items: [
    BottomNavItem(icon: Icons.home, label: '홈'),
    BottomNavItem(icon: Icons.search, label: '검색'),
    BottomNavItem(icon: Icons.person, label: '프로필'),
  ],
)

Props / Parameters#

속성타입기본값설명
titleWidget?null네비게이션 제목
leadingWidget?null좌측 위젯
actions List<Widget>? null 우측 액션 위젯 목록
elevatedbooltrue그림자 표시

BottomNav#

속성타입기본값설명
currentIndexint0현재 선택 인덱스
onTap ValueChanged<int>? null 탭 콜백
items List<BottomNavItem> 필수 네비게이션 항목

변형 (Variants)#

사이드 네비게이션#

SideNav(
  currentIndex: selectedIndex,
  onTap: handleNavTap,
  items: [
    SideNavItem(icon: Icons.dashboard, label: '대시보드'),
    SideNavItem(icon: Icons.analytics, label: '분석'),
    SideNavItem(icon: Icons.settings, label: '설정'),
  ],
)

배지 포함 네비게이션#

BottomNavItem(
  icon: Icons.notifications,
  label: '알림',
  badge: Badge.count(count: 5),
)

동작 스펙 (Behavior)#

컨테이너 타입#

타입클래스방향용도
BarNavigationBar수평/수직하단 탭, 상단 바
RailNavigationRail수직좁은 사이드바 (아이콘 위주)
SidebarNavigationSidebar수직넓은 사이드바 (아이콘 + 라벨)

라벨 표시 모드#

// 모든 항목에 라벨 표시
NavigationBar(labelType: NavigationLabelType.all, ...)

// 선택된 항목만 라벨 표시
NavigationBar(labelType: NavigationLabelType.selected, ...)

// 확장 시만 라벨 표시 (Sidebar)
NavigationSidebar(labelType: NavigationLabelType.expanded, ...)

// 호버 시 툴팁으로 표시
NavigationRail(labelType: NavigationLabelType.tooltip, ...)

// 라벨 없음
NavigationBar(labelType: NavigationLabelType.none, ...)

라벨 위치#

  • NavigationLabelPosition.bottom: 아이콘 아래 (기본)
  • NavigationLabelPosition.end: 아이콘 옆 (사이드바)
  • NavigationLabelPosition.start: 아이콘 앞
  • NavigationLabelPosition.top: 아이콘 위

라벨 오버플로우#

  • NavigationOverflow.marquee: 긴 텍스트 스크롤 (기본)
  • NavigationOverflow.ellipsis: 말줄임표
  • NavigationOverflow.clip: 잘라내기
  • NavigationOverflow.none: 오버플로우 허용

선택 상태#

  • Flutter: onSelected 콜백으로 인덱스 전달, selectedStyle로 선택 스타일 변경
  • Web: currentIndex prop으로 활성 항목 결정, CSS bg-accent text-accent-foreground 적용

Surface 효과#

NavigationBar(
  surfaceBlur: 10.0,
  surfaceOpacity: 0.8,
  backgroundColor: Colors.white.withOpacity(0.5),
  children: items,
)

항목 구성 요소#

  • NavigationItem: 선택 가능한 항목 (아이콘 + 라벨)
  • NavigationButton: 선택 불가, 클릭 액션만
  • NavigationLabel: 섹션 레이블 (비선택)
  • NavigationDivider: 구분선
  • NavigationGap: 간격

사용 가이드라인 (Usage Guidelines)#

✅ Do#

네비게이션 항목을 3~5개로 유지하세요.

BottomNav(
  currentIndex: selectedIndex,
  onTap: handleNavTap,
  items: [
    BottomNavItem(icon: Icons.home, label: '홈'),
    BottomNavItem(icon: Icons.search, label: '검색'),
    BottomNavItem(icon: Icons.notifications, label: '알림'),
    BottomNavItem(icon: Icons.person, label: '프로필'),
  ],
)

5개 이상은 항목이 작아져 터치가 어렵습니다.


❌ Don't#

네비게이션에 텍스트만 사용하지 마세요.

아이콘 없이 텍스트만 나열하면 빠른 인식이 어렵습니다. 아이콘 + 라벨 조합이 최적입니다.

✅ Do#

화면 크기에 맞는 네비게이션 타입을 선택하세요.

// 모바일: 하단 네비게이션
NavigationBar(direction: Axis.horizontal, children: items)

// 태블릿: 네비게이션 레일
NavigationRail(children: items)

// 데스크톱: 사이드바
NavigationSidebar(expanded: true, children: items)

❌ Don't#

데스크톱에서 하단 네비게이션을 사용하지 마세요.

넓은 화면에서는 사이드바가 공간 활용과 정보 접근성 면에서 우수합니다.

✅ Do#

알림 배지를 적절히 사용하세요.

BottomNavItem(
  icon: Icons.notifications,
  label: '알림',
  badge: Badge.count(count: 5),
)

읽지 않은 항목 수를 표시하여 사용자의 주의를 끕니다.

접근성 (Accessibility)#

키보드 인터랙션#

동작
Tab네비게이션 항목 간 포커스 이동
Enter / Space포커스된 항목 선택
/ 수평 네비게이션에서 항목 이동
/ 수직 네비게이션에서 항목 이동

스크린 리더#

  • Flutter: 각 항목의 라벨과 선택 상태가 읽힘. NavigationLabel은 섹션 헤더로 인식
  • Web: <nav> 태그 컨테이너. 활성 항목에 시각적 + 시맨틱 구분

최소 터치 영역#

  • 하단 네비게이션 항목: 최소 44×44dp
  • 사이드바 항목: 전체 너비 클릭 가능

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

항목FlutterWeb
컨테이너NavigationBar, NavigationRail, NavigationSidebarNavigationBar (단일)
항목 NavigationItem, NavigationButton, NavigationLabel, etc. NavigationItem (icon + label)
라벨 모드6종 (all, selected, expanded, tooltip, none)없음 (항상 표시)
라벨 위치4종 (top, bottom, start, end)고정 (아이콘 아래)
정렬NavigationBarAlignment (6종)CSS flex justify-around
Surface 효과surfaceBlur, surfaceOpacity미지원
확장/축소expanded prop (Sidebar)미지원
오버플로우marquee, ellipsis, clip, none미지원
테마NavigationBarThemeTailwind CSS
태그Widget<nav> HTML
  • Tabs: 인페이지 콘텐츠 전환. Navigation은 앱 전체 섹션 이동
  • Menu: 액션 메뉴. Navigation은 목적지 이동, Menu는 액션 실행
  • Drawer: 사이드 패널. 모바일에서 네비게이션을 Drawer에 담을 수 있음

조합 예제#

// 반응형 네비게이션 패턴
LayoutBuilder(
  builder: (context, constraints) {
    final isDesktop = constraints.maxWidth >= 1024;
    final isTablet = constraints.maxWidth >= 768;

    return Row(children: [
      if (isDesktop)
        NavigationSidebar(
          expanded: true,
          index: selectedIndex,
          onSelected: handleNavSelect,
          children: [
            NavigationLabel(child: Text('메뉴')),
            NavigationItem(child: Icon(Icons.home), label: Text('홈')),
            NavigationItem(child: Icon(Icons.analytics), label: Text('분석')),
            NavigationDivider(),
            NavigationItem(child: Icon(Icons.settings), label: Text('설정')),
          ],
        )
      else if (isTablet)
        NavigationRail(
          index: selectedIndex,
          onSelected: handleNavSelect,
          children: navItems,
        ),
      Expanded(child: pageContent),
    ]);
  },
)