Flutter for OpenHarmony 환경에서 하단 탐색 바를 구현할 때, PageView와 BottomNavigationBar의 조합은 강력하면서도 직관적인 선택입니다. 이 글에서는 실제 프로젝트에서 마주칠 수 있는 문제점과 해결 방안을 중점적으로 다루며, 성능 최적화 팁까지 제공합니다.
1. PageView + BottomNavigationBar 구조 선택 이유
PageView를 사용하면 각 탭의 상태(State)를 유지하면서 페이지 전환 애니메이션을 자연스럽게 적용할 수 있습니다. BottomNavigationBar와 결합하면 사용자 인터랙션에 따른 페이지 이동이 일관성 있게 동작합니다.
class _MainScreenState extends State<MainScreen> {
int _selectedIndex = 0;
final PageController _pageCtrl = PageController();
final List<Widget> _pageList = [
const HomePage(),
const MessagePage(),
const WorkPage(),
const DiscoverPage(),
const ProfilePage(),
];
void _onTabTapped(int idx) {
if (_selectedIndex != idx) {
_pageCtrl.animateToPage(
idx,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
controller: _pageCtrl,
onPageChanged: (idx) {
setState(() {
_selectedIndex = idx;
});
},
children: _pageList,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
onTap: _onTabTapped,
items: [
// 각 아이템 정의
],
),
);
}
}
2. 자주 발생하는 문제와 해결책
이슈 1: 슬라이드와 탭 선택 간 상태 불일치
사용자가 PageView를 직접 슬라이드할 때 BottomNavigationBar의 index가 업데이트되지 않는 현상입니다. onPageChanged 콜백에서 setState를 호출하여 현재 인덱스를 동기화하면 해결됩니다.
이슈 2: Controller 미해제로 인한 메모리 누수
PageController는 dispose() 메서드에서 반드시 해제해야 합니다. 그렇지 않으면 앱이 점점 느려지거나 비정상 종료될 수 있습니다.
@override
void dispose() {
_pageCtrl.dispose();
super.dispose();
}
이슈 3: 네트워크 요청 타임아웃
OpenHarmony 환경에서 dio 라이브러리 사용 시 연결 타임아웃이 자주 발생합니다. 충분한 타임아웃 값을 설정하고, 네트워크 권한을 명시해야 합니다.
final Dio httpClient = Dio(
BaseOptions(
connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 30),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
),
);
또한 module.json5에 인터넷 권한이 선언되어 있는지 확인하세요.
{
"module": {
"requestPermissions": [
{"name": "ohos.permission.INTERNET"}
]
}
}
이슈 4: 아이콘 스타일 불일치
선택/비선택 아이콘의 디자인이 다르면 사용자 경험에 부정적 영향을 줍니다. activeIcon 속성을 활용하여 두 상태의 아이콘 스타일을 통일하세요.
BottomNavigationBarItem(
icon: Icon(Icons.home_outlined),
activeIcon: Icon(Icons.home),
label: '홈',
),
3. 성능 최적화 전략
- 상태 보존: 각 탭을 StatefulWidget으로 구현하여 페이지 전환 시 데이터가 유지되도록 합니다.
- 애니메이션 지속 시간: 300ms는 자연스러운 전환을 위한 최적의 길이입니다. 너무 빠르면 어색하고, 너무 느리면 지루합니다.
- 리스트 최적화: 긴 목록을 표시할 때는
ListView.builder를 사용하여 화면에 보이는 항목만 렌더링합니다. 메모리 사용량이 크게 줄어듭니다.
4. 실행 결과
위 코드를 적용하면 다섯 개의 탭이 부드럽게 전환되며, 각 페이지의 상태가 유지됩니다. BottomNavigationBar와 PageView의 인덱스가 항상 일치하여 사용자 혼란을 방지합니다.
5. 핵심 요약
- PageController의
onPageChanged로 상태 동기화 - Controller는 반드시
dispose()에서 해제 - 네트워크 타임아웃 설정과 권한 확인
- 선택/비선택 아이콘 스타일 통일
- ListView.builder로 렌더링 효율화
이 가이드를 따라 구현하면 Flutter for OpenHarmony에서 안정적이고 반응성 좋은 하단 탐색 바를 만들 수 있습니다. 전체 소스 코드는 AtomGit 저장소(https://atomgit.com)에서 확인할 수 있습니다.