Flutter BottomNavigation을 직접 구현해보자

[Flutter] · 2024. 8. 2. 19:56

내가 바텀네비게이션을 직접 구현하게 된 이유는 

 

  • 플러터에서 기본적으로 제공하는 BottomNavigationBar가 있는데 너무 구려서 못써먹겠다.
  • 디자이너가 요구하는 디자인에 다 맞춰보니 자꾸 layout overflow가 생기는데 이럴거면 그냥 직접 만드는게 낫겠다 싶다.

 

한번 만들어 보니 그 방법은 의외로 간단했다. 그래서 어거지고 기본 네비게이션을 쓰려고 한 내 시간이 너무 아까웠다.

 

Scaffold(
  body: Stack(
    children: [
      IndexedStack(
        index: _selectedIndex,
        children: [
          Bookmarkscreen(),
          Mapscreen(),
          Menuscreen(),
        ],
      ),
      Positioned(
        left: screenWidth * 0.1,
        right: screenWidth * 0.1,
        bottom: MediaQuery.of(context).size.height * 0.055,
        child: bottomNavigation(screenSize),
      ),
    ],
  ),
);

 

IndexedStack을 사용하여 페이지를 렌더링 한다.

디자이너가 화면 하단에서 살짝띄운 네비게이션을 원해서 Scaffold body 위에 Stack위젯으로 화면과 네비를 동시에 띄웠다.

 

Widget bottomNavigation(Size screensize) {
  return Container(
    width: screensize.width * 300 / 375,
    height: screensize.height * 80 / 812,
    decoration: const BoxDecoration(
        image: DecorationImage(
            image: AssetImage('assets/images/navigation.png')
        ),
    ),
    child: Row(
      children: [
        GestureDetector(
          child: Container(
            padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 22),
            width: 100,
            height: 80,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                SvgPicture.asset(
                  _selectedIndex == 0
                      ? 'assets/images/btn_plug.svg'
                      : 'assets/images/ic_star.svg',
                  width: _selectedIndex == 0 ? 56 : 28,
                  height: _selectedIndex == 0 ? 56 : 28,
                ),
                Visibility(
                  visible: _selectedIndex == 0 ? false : true,
                  child: Text(
                    '저장',
                    style: B5_KR_Rg_12.copyWith(color: gray_300),
                    textAlign: TextAlign.center,
                  ),
                )
              ],
            ),
          ),
          onTap: () {
            _onItemTapped(0);
          },
        ),
        GestureDetector(
          child: Container(
            padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 22),
            width: 100,
            height: 80,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                SvgPicture.asset(
                    _selectedIndex == 1 ? 'assets/images/btn_home.svg': 'assets/images/ic_plug.svg',
                    width:  _selectedIndex == 1 ? 56 : 28,
                    height:  _selectedIndex == 1 ? 56 : 28),
                Visibility(
                  visible: _selectedIndex == 1 ? false : true,
                  child: Text(
                    '홈',
                    style: B5_KR_Rg_12.copyWith(color: gray_300),
                    textAlign: TextAlign.center,
                  ),
                )
              ],
            ),
          ),
          onTap: () {
            _onItemTapped(1);
          },
        ),
        GestureDetector(
          child: Container(
            padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 22),
            width: 100,
            height: 80,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                SvgPicture.asset(
                    _selectedIndex == 2 ? 'assets/images/btn_menu.svg': 'assets/images/ic_menu.svg',
                    width:  _selectedIndex == 2 ? 56 : 28,
                    height:  _selectedIndex == 2 ? 56 : 28),
                Visibility(
                  visible: _selectedIndex == 2 ? false : true,
                  child: Text(
                    '메뉴',
                    style: B5_KR_Rg_12.copyWith(color: gray_300),
                    textAlign: TextAlign.center,
                  ),
                )
              ],
            ),
          ),
          onTap: () {
            _onItemTapped(2);
          },
        )
      ],
    ),
  );
}

 

코드가 길긴하지만 별거 없고 onTab에서 

void _onItemTapped(int index) {
  setState(() {
    _selectedIndex = index;
  });
}

 

선택한 화면 index를 넘겨주는 메서드를 만들어서 사용해주었다.

 

SvgPicture.asset(
  _selectedIndex == 0
      ? 'assets/images/btn_plug.svg'
      : 'assets/images/ic_star.svg',
  width: _selectedIndex == 0 ? 56 : 28,
  height: _selectedIndex == 0 ? 56 : 28,
),
Visibility(
  visible: _selectedIndex == 0 ? false : true,
  child: Text(
    '저장',
    style: B5_KR_Rg_12.copyWith(color: gray_300),
    textAlign: TextAlign.center,
  ),
)

 

또한 선택시에 1. 밑에 title이 없어짐, 2. 이미지가 바뀜, 3. 이미지가 커짐을 구현했다.

 

앞으로 디자인이 빡세면 기본을 쓰는거보다 직접구현하는 게 학습적으로도  좋아보인다.