
일단 바텀 네비게이션바의 홈은
탭바
를 갖고 있고, 각 탭바는 탭바 뷰를 갖고 있다.이때 알아야 되는건,
탭바 뷰
는 Page 가 아니고 컴포넌트라고 생각해야 한다.
탭바가 움직일 때 모든 페이지가 랜더링 되는 것이 아니라 오렌지색인 내서재
에 해당하는 곳이 랜더링 되는 것이기 때문이다.1. 기본 코드
class MainPage extends StatefulWidget {
const MainPage({super.key});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage>
with SingleTickerProviderStateMixin {
late TabController controller; //탭의 컨트롤러
int _currentTab = 0; // 바텀네비바의 인덱스
// 각 탭의 제목 리스트
final List<String> titles = ['홈', '내서재', '검색', '설정'];
@override
void initState() {
super.initState();
controller = TabController(length: 4, vsync: this); // 탭바를 정의한다.
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
// AppBar의 title을 현재 선택된 탭의 제목으로 설정
appBar: CustomAppBar(
titleText: titles[_currentTab],
),
body: IndexedStack(
index: _currentTab,
children: [
// 하위 항목은 모두 bottomNavigationBar 의 인덱스 페이지
HomePage(), // 탭바를 갖고 있다.
LibraryPage(),
SearchPage(),
SettingPage(),
],
),
bottomNavigationBar: _bottomNavigation());
}
}
_bottomNavigation 위젯 코드
BottomNavigationBar _bottomNavigation() {
return BottomNavigationBar(
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.black38,
selectedFontSize: 10,
unselectedFontSize: 10,
type: BottomNavigationBarType.fixed,
onTap: (index) {
setState(() {
_currentTab = index;
});
},
//현재 선택된 index 지정
currentIndex: _currentTab,
items: [
const BottomNavigationBarItem(
label: '홈',
icon: Icon(CupertinoIcons.house),
),
const BottomNavigationBarItem(
label: '내서재',
icon: Icon(CupertinoIcons.book),
),
const BottomNavigationBarItem(
label: '검색',
icon: Icon(CupertinoIcons.search),
),
const BottomNavigationBarItem(
label: '설정',
icon: Icon(CupertinoIcons.settings),
),
],
);
}
이때 큰 문제가 발생한다.
제일 처음 호출되는
MainPage
는 HomePage(), LibraryPage(), SearchPage(), SettingPage(), 를 갖고 있기 때문에 한번에 모든 페이지를 가져오려고 한다. (로딩이 100년 걸리게 된다.)
그렇기 때문에 필요한 페이지만 가져오는 알고리즘을 추가해야 한다.2. 로딩 최적화
var loadPages = [0];
를 추가한다. loadPages
는 현재 로딩 되어 진 페이지를 저장하는 곳이다.
기본적으로 home 을 갖고있게 되는데 (0번) 얘는 로딩시 처음 뜨는 곳이라 바로 넣어두었다. children: [
loadPages.contains(0) ? const HomePage() : Container(),
loadPages.contains(1) ? const LibraryPage() : Container(),
loadPages.contains(2) ? const SearchPage() : Container(),
loadPages.contains(3) ? const SettingPage() : Container(),
],
배열에 해당 인덱스가 들어있다면 페이지를 랜더링 하고, 아닐 시 빈 Container 를 만든다.
이후 탭을 눌렀을 때 배열에 인덱스를 추가하여 랜더링 할 수 있도록 하고,
const
를 사용하여 이미 랜더링 되었던 화면은 다시 그리지 않도록 하였다.
전체코드
import 'package:bookbox/ui/components/custom_app_bar.dart';
import 'package:bookbox/ui/main/home/home_page.dart';
import 'package:bookbox/ui/main/library/library_page.dart';
import 'package:bookbox/ui/main/search/search_page.dart';
import 'package:bookbox/ui/main/setting/setting_page.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class MainPage extends StatefulWidget {
const MainPage({super.key});
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage>
with SingleTickerProviderStateMixin {
late TabController controller;
int _currentTab = 0;
var loadPages = [0]; //저장되는 곳
// 각 탭의 제목 리스트
final List<String> titles = ['홈', '내서재', '검색', '설정'];
@override
void initState() {
super.initState();
controller = TabController(length: 4, vsync: this);
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
// AppBar의 title을 현재 선택된 탭의 제목으로 설정
appBar: CustomAppBar(
titleText: titles[_currentTab],
),
//titleText: titles[index],
body: IndexedStack(
index: _currentTab,
children: [
loadPages.contains(0) ? const HomePage() : Container(),
loadPages.contains(1) ? const LibraryPage() : Container(),
loadPages.contains(2) ? const SearchPage() : Container(),
loadPages.contains(3) ? const SettingPage() : Container(),
],
),
bottomNavigationBar: _bottomNavigation());
}
BottomNavigationBar _bottomNavigation() {
return BottomNavigationBar(
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.black38,
selectedFontSize: 10,
unselectedFontSize: 10,
type: BottomNavigationBarType.fixed,
currentIndex: _currentTab,
onTap: (index) {
var pages = loadPages;
if (!pages.contains(index)) {
pages.add(index);
print(pages);
}
_currentTab = index;
loadPages = pages;
setState(() {});
},
//현재 선택된 index 지정
items: [
const BottomNavigationBarItem(
label: '홈',
icon: Icon(CupertinoIcons.house),
),
const BottomNavigationBarItem(
label: '내서재',
icon: Icon(CupertinoIcons.book),
),
const BottomNavigationBarItem(
label: '검색',
icon: Icon(CupertinoIcons.search),
),
const BottomNavigationBarItem(
label: '설정',
icon: Icon(CupertinoIcons.settings),
),
],
);
}
}
패키지


패키지는 각 페이지이고,
패키지 내부에 탭을 위한 패키지를 생성했다. 이제 내부 요소를 만들면 된다.
Share article