Navigation#
앱의 주요 탐색을 위한 네비게이션 바 컴포넌트입니다.
Live Preview#
Web
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#
NavBar#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
title | Widget? | null | 네비게이션 제목 |
leading | Widget? | null | 좌측 위젯 |
actions |
List<Widget>? |
null |
우측 액션 위젯 목록 |
elevated | bool | true | 그림자 표시 |
BottomNav#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
currentIndex | int | 0 | 현재 선택 인덱스 |
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)#
컨테이너 타입#
| 타입 | 클래스 | 방향 | 용도 |
|---|---|---|---|
| Bar | NavigationBar | 수평/수직 | 하단 탭, 상단 바 |
| Rail | NavigationRail | 수직 | 좁은 사이드바 (아이콘 위주) |
| Sidebar | NavigationSidebar | 수직 | 넓은 사이드바 (아이콘 + 라벨) |
라벨 표시 모드#
// 모든 항목에 라벨 표시
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:
currentIndexprop으로 활성 항목 결정, CSSbg-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)#
| 항목 | Flutter | Web |
|---|---|---|
| 컨테이너 | NavigationBar, NavigationRail, NavigationSidebar | NavigationBar (단일) |
| 항목 | 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 | 미지원 |
| 테마 | NavigationBarTheme | Tailwind CSS |
| 태그 | Widget | <nav> HTML |
관련 컴포넌트 (Related Components)#
- 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),
]);
},
)