inblog logo
|
[HootJem] 개발 기록 블로그
    Projectflutter

    [BookBox] 패키지 구조와 로딩 알고리즘

    HootJem's avatar
    HootJem
    Dec 04, 2024
    [BookBox] 패키지 구조와 로딩 알고리즘
    Contents
    1. 기본 코드2. 로딩 최적화
     
    BottomNavi 는 알 고 있었는데 상단과 하단에 동시에 버튼이 존재한다면 어떻게 처리해야하는지 굉장한 혼란에 빠졌다. TopNavi 라는 검색도 해보고 여러가지 찾아보다가 생각해 보니 프로필 앱을 만들면서 탭바 라는 기능을 사용한 적이 있는 것이다.
    https://inblog.ai/hj/4-프로필-앱-만들기-31312?traffic_type=internal
    이를 활용하여 아래처럼 페이지를 구성하였다.
     
    notion image
    일단 바텀 네비게이션바의 홈은 탭바 를 갖고 있고, 각 탭바는 탭바 뷰를 갖고 있다.
    이때 알아야 되는건, 탭바 뷰 는 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 를 사용하여 이미 랜더링 되었던 화면은 다시 그리지 않도록 하였다.
     
    notion image
     

    전체코드

    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), ), ], ); } }
     

    패키지

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

    데이터를 불러온 뒤 생각해 보니, 이미 index 에 저장된 요소를 불러오기 때문에 그 사이에 업로드가 발생한다면 사용자는 그것을 볼 수 없는 문제가 있다
    따라서 상단으로 스크롤 하면 새로운 데이터를 가져오는 기능이 추가로 필요하다.
    Share article
    Contents
    1. 기본 코드2. 로딩 최적화

    [HootJem] 개발 기록 블로그

    RSS·Powered by Inblog