NavigationBar#
앱의 주요 섹션 간 이동을 위한 하단 네비게이션 바 컴포넌트입니다. 아이콘과 레이블을 조합하여 현재 위치를 시각적으로 표시합니다.
Live Preview#
Web
Flutter
NavigationBar(
items: [
NavigationItem(label: 'Home'),
NavigationItem(label: 'Search'),
NavigationItem(label: 'Profile'),
],
currentIndex: 0,
)
NavigationBar(
index: 0,
onSelected: handleSelected,
children: [
NavigationItem(child: Text('Home')),
NavigationItem(child: Text('Search')),
NavigationItem(child: Text('Profile')),
],
)
사용 시기 (When to Use)#
이 컴포넌트를 사용하세요:
- 모바일 앱의 주요 3~5개 섹션 간 이동이 필요할 때
- 사용자가 어느 화면에서든 빠르게 주요 섹션으로 이동할 수 있어야 할 때
- 현재 선택된 섹션을 항상 표시해야 할 때
대신 다른 컴포넌트를 사용하세요:
Navigation: 사이드바 형태의 네비게이션이 필요할 때Tabs: 동일 화면 내에서 콘텐츠 탭을 전환할 때Switcher: 보기 모드 전환 등 로컬 전환에
기본 사용법 (Basic Usage)#
// 기본 네비게이션 바
NavigationBar(
currentIndex: _selectedIndex,
onTap: handleTabChanged,
items: [
NavigationBarItem(
icon: Icon(Icons.home_outlined),
activeIcon: Icon(Icons.home),
label: '홈',
),
NavigationBarItem(
icon: Icon(Icons.search_outlined),
activeIcon: Icon(Icons.search),
label: '검색',
),
NavigationBarItem(
icon: Icon(Icons.person_outlined),
activeIcon: Icon(Icons.person),
label: '프로필',
),
],
)
// Outlined 변형, 레이블 숨김
NavigationBar(
currentIndex: _selectedIndex,
onTap: handleTabChanged,
variant: NavigationBarVariant.outlined,
showLabels: false,
items: [
NavigationBarItem(icon: Icon(Icons.home_outlined), label: '홈'),
NavigationBarItem(icon: Icon(Icons.search_outlined), label: '검색'),
NavigationBarItem(icon: Icon(Icons.settings_outlined), label: '설정'),
],
)
NavigationBar(
currentIndex: _selectedIndex,
onTap: handleTabChanged,
items: [
NavigationBarItem(icon: Icon(Icons.home), label: '홈'),
NavigationBarItem(icon: Icon(Icons.search), label: '검색'),
NavigationBarItem(icon: Icon(Icons.person), label: '프로필'),
],
)
NavigationBar(
currentIndex: _selectedIndex,
onTap: handleTabChanged,
variant: NavigationBarVariant.outlined,
showLabels: true,
items: [
NavigationBarItem(icon: Icon(Icons.home), label: '홈'),
NavigationBarItem(icon: Icon(Icons.search), label: '검색'),
],
)
Props / Parameters#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
items |
List<NavigationBarItem> |
필수 | 네비게이션 아이템 목록 |
currentIndex |
int |
0 |
현재 선택된 아이템 인덱스 |
onTap |
void Function(int)? |
null |
아이템 탭 핸들러 |
variant |
NavigationBarVariant |
filled |
스타일 변형 |
showLabels |
bool |
true |
레이블 표시 여부 |
NavigationBarItem#
| 속성 | 타입 | 기본값 | 설명 |
|---|---|---|---|
icon | Widget | 필수 | 비활성 상태 아이콘 |
activeIcon |
Widget? |
null |
활성 상태 아이콘 (미지정 시 icon 사용) |
label | String | 필수 | 아이템 레이블 텍스트 |
변형 (Variants)#
Filled#
배경색이 채워진 기본 변형입니다. 선택된 아이템의 배경이 강조됩니다.
NavigationBar(
variant: NavigationBarVariant.filled,
currentIndex: 0,
onTap: handleTabChanged,
items: [...],
)
Outlined#
테두리만 있는 변형입니다. 미니멀한 디자인에 적합합니다.
NavigationBar(
variant: NavigationBarVariant.outlined,
currentIndex: 0,
onTap: handleTabChanged,
items: [...],
)
동작 스펙 (Behavior)#
인터랙션#
- 탭: 항목 탭 시
onTap(index)호출 - 호버: 항목 호버 시 배경색 약한 강조
상태 전환#
inactive→active: 다른 항목 탭 시 현재 항목 활성화, 이전 항목 비활성화activeIcon이 있는 경우 활성 시 아이콘 교체
애니메이션#
- 활성 항목 전환: 배경색 변경 200ms ease-in-out
- 아이콘 교체: 크로스 페이드 150ms
사용 가이드라인 (Usage Guidelines)#
✅ Do#
항목 수를 3~5개로 제한
CouiNavigationBar(
currentIndex: _selectedIndex,
onTap: handleTabChanged,
items: [
NavigationBarItem(icon: Icon(Icons.home_outlined), label: '홈'),
NavigationBarItem(icon: Icon(Icons.explore_outlined), label: '탐색'),
NavigationBarItem(icon: Icon(Icons.notifications_outlined), label: '알림'),
NavigationBarItem(icon: Icon(Icons.person_outlined), label: '프로필'),
],
)
항목이 너무 많으면 각 아이콘이 너무 작아지고 레이블이 잘린다. 3~5개가 최적이다.
❌ Don't#
6개 이상의 항목을 NavigationBar에 넣지 않기
// ❌ 너무 많은 항목
CouiNavigationBar(
items: List.generate(7, (i) => NavigationBarItem(
icon: Icon(Icons.circle),
label: '메뉴 ${i + 1}',
)),
currentIndex: 0,
onTap: handleTabChanged,
)
항목이 6개를 초과하면 레이블이 잘리거나 아이콘이 너무 작아진다.
✅ Do#
활성/비활성 아이콘을 쌍으로 제공
NavigationBarItem(
icon: Icon(Icons.home_outlined), // 비활성: 윤곽선
activeIcon: Icon(Icons.home), // 활성: 채움
label: '홈',
)
채워진 아이콘과 윤곽선 아이콘을 쌍으로 사용하면 선택 상태가 더 명확하게 전달된다.
❌ Don't#
레이블 없이 아이콘만 사용하는 경우 semanticLabel 생략
// ❌ showLabels: false인데 semanticLabel 없음
NavigationBarItem(
icon: Icon(Icons.home_outlined),
label: '홈', // showLabels: false라도 label은 접근성에 사용됨
)
showLabels: false여도 label은 스크린 리더용으로 반드시 설정해야 한다.
✅ Do#
네비게이션 바에는 3~5개의 주요 섹션만 배치하세요.
CouiNavigationBar(
items: [
NavigationBarItem(icon: Icons.home, label: '홈'),
NavigationBarItem(icon: Icons.search, label: '검색'),
NavigationBarItem(icon: Icons.favorite, label: '찜'),
NavigationBarItem(icon: Icons.person, label: '프로필'),
],
currentIndex: currentTab,
onChanged: handleTabChanged,
)
3~5개의 주요 섹션이 탐색 구조를 명확하게 유지하고 사용자가 빠르게 이동할 수 있습니다.
❌ Don't#
네비게이션 바에 중요도가 낮은 페이지를 포함하지 마세요.
// ❌ 덜 중요한 페이지까지 네비게이션 바에 포함
CouiNavigationBar(
items: [
NavigationBarItem(icon: Icons.home, label: '홈'),
NavigationBarItem(icon: Icons.settings, label: '설정'), // 덜 중요
NavigationBarItem(icon: Icons.info, label: '약관'), // 거의 안 씀
NavigationBarItem(icon: Icons.help, label: '도움말'), // 거의 안 씀
],
)
네비게이션 바는 자주 사용하는 핵심 기능에만 사용해야 합니다. 설정이나 도움말은 메뉴나 프로필 화면에서 접근하도록 하세요.
접근성 (Accessibility)#
키보드 인터랙션#
| 키 | 동작 |
|---|---|
Tab | 다음 네비게이션 항목으로 포커스 |
Enter / Space | 포커스된 항목 활성화 |
Arrow Left/Right | 항목 간 이동 |
스크린 리더#
- Flutter:
Semantics(selected: isActive, label: itemLabel)자동 적용 -
Web:
role="navigation"+aria-label="메인 네비게이션"+ 각 항목aria-current="page"적용
터치 타겟#
- 각 항목 최소 터치 타겟: 48x48dp
크로스 플랫폼 차이점 (Platform Differences)#
| 항목 | Flutter | Web |
|---|---|---|
| 클래스명 | CouiNavigationBar | NavigationBar |
| 탭 핸들러 | onTap | onTap |
| 위치 | Scaffold.bottomNavigationBar |
화면 하단 고정 (position: fixed) |
| 배경 블러 | 지원 (BackdropFilter) | CSS backdrop-filter |
관련 컴포넌트 (Related Components)#
- Navigation: 사이드바/상단 바 형태의 네비게이션
- Tabs: 화면 내 콘텐츠 섹션 전환에 사용
조합 예제#
// Scaffold와 NavigationBar 조합
Scaffold(
body: IndexedStack(
index: _selectedIndex,
children: [
const HomeScreen(),
const SearchScreen(),
const ProfileScreen(),
],
),
bottomNavigationBar: CouiNavigationBar(
currentIndex: _selectedIndex,
onTap: handleTabChanged,
items: [
NavigationBarItem(
icon: Icon(Icons.home_outlined),
activeIcon: Icon(Icons.home),
label: '홈',
),
NavigationBarItem(
icon: Icon(Icons.search_outlined),
activeIcon: Icon(Icons.search),
label: '검색',
),
NavigationBarItem(
icon: Icon(Icons.person_outlined),
activeIcon: Icon(Icons.person),
label: '프로필',
),
],
),
)