본문 바로가기

일::개발

Flutter: BottomNavigationBar 색 설정하기

BottomNavigationBar 의 Icon, Label 색 설정하는 것이 간단한 것 같으면서도 복잡하다.

Light Mode, Dark Mode 생각하면 각각의 color scheme 만들어서 Theme에서 설정하고 위젯 단에서는 색을 지정하지 않고 싶은데, 이게 잘 안 된다.

 

BottomNavigationBar의 Icon, Label 색은 어떻게 결정되는지 파보면 될 것 같다.

 

일단 BottomNavigationBar의 구성부터 보면

 

BottomNavigationBar

+ Row

   + [_BottomNavigationTile]

      + _Tile

 

잡스러운 것들 빼고 나면 대략 이렇게 구성되어 있다.

Icon 과 Label 로 구성된 Column 이 _Tile 이고

_Tile의 스타일과 터치 효과 같은 것을 지정한 것이 _BottomNavigationTile 이다.

_BottomNavigationTile 의 Row 가 BottomNavigationBar 라고 보면 되겠다.

 

일단 필요한 것은 Icon과 Labal의 색이 어떻게 정해지는가 하는 것이다.

 

_BottomNavigationTile 에서 _Tile을 만드는 부분을 보면

 

_Tile(
  layout: layout,
  icon: _TileIcon(
    colorTween: colorTween!,
    animation: animation,
    iconSize: iconSize,
    selected: selected,
    item: item,
    selectedIconTheme: selectedIconTheme,
    unselectedIconTheme: unselectedIconTheme,
  ),
  label: _Label(
    colorTween: colorTween!,
    animation: animation,
    item: item,
    selectedLabelStyle: selectedLabelStyle,
    unselectedLabelStyle: unselectedLabelStyle,
    showSelectedLabels: showSelectedLabels,
    showUnselectedLabels: showUnselectedLabels,
  ),
),

 

이렇게 되어 있다.

icon은 selected, unselected 의 IconTheme 을 별도로 지정하게 되어 있고,

label은 selected, unselected 의 Style 을 별도로 지정하고 있다.

 

그렇다면 selectedIconTheme, unselectedIconTheme, selectedLabelStyle, unselectedLabelStyle 이 각각 어떻게 지정되는지만 찾아보면 될 것 같다.

 

BottomNavigationBar 위젯의 build() 함수부터 추적해보자.

 

selectedIconTheme: widget.selectedIconTheme ?? bottomTheme.selectedIconTheme,
unselectedIconTheme: widget.unselectedIconTheme ?? bottomTheme.unselectedIconTheme,
selectedLabelStyle: effectiveSelectedLabelStyle,
unselectedLabelStyle: effectiveUnselectedLabelStyle,

iconTheme 은 BottomNavigationBar 위젯을 만들때 파라미터로 주지 않으면 bottomTheme (BottomNavigationBarTheme.of(context)) 으로 정해진다. 

 

BottomNavigationBar 만들 때나 앱 전체의 Theme 지정할 때

 

selectedIconTheme: IconThemeData(color: Theme.of(context).primaryColor),
unselectedIconTheme: IconThemeData(color: Theme.of(context).colorScheme.secondary), // 원하는 색 지정

 

이렇게 넣어주면 되겠다.

 

그러나! 여기서 지정하는 스타일은 IconTheme() 으로 적용되기 때문에 Image.asset() 을 사용하는 경우는 물론이고, svg 를 사용하는 경우에도 적용되지 않는다.

 

Light Mode, Dark Mode 일관성 있는 적용을 위해 svg 이미지를 사용하는 경우에는 BottomNavigationBarItem 을 정의할 때 색을 별도로 지정해 주어야 한다.

 

BottomNavigationBarItem(
    icon: Container(
      margin: const EdgeInsets.only(bottom: 4),
      child: SvgPicture.asset(
        svgFilePath,
        width: 20,
        height: 20,
        color: Theme.of(context).colorScheme.secondary,
      ),
    ),
    activeIcon: Container(
      margin: const EdgeInsets.only(bottom: 4),
      child: SvgPicture.asset(
        svgFilePath,
        width: 22,
        height: 22,
        color: Theme.of(context).colorScheme.primary,
      ),
    ),
    label: MainMenu.values[index].label,
  )

 

 

label의 경우에는 조금 더 복잡하다.

label 의 색을 설정할 수 있을 것 같은 곳은

 

BottomNavigationBarThemeData() 에만 봐도 unselectedItemColor, unselectedLabelStyle 이 있고,

BottomNavigationBar() 를 만들 때도 똑같이 unselectedItemColor, unselectedLabelStyle 이 있다.

 

_Tile을 만들 때, _Label 에는 colorTween, selectedLabelStyle, unselectedLabelStyle 이 파라미터로 전달된다.

 

실제로 label 텍스트를 그리는 코드를 보면, selected, unselected 전환 효과를 나타내기 위해서 색깔을 제외한 다른 부분은 selectedLabelStyle, unselectedLabelStyle 을 사용하고 색은 colorTween 을 이용해서 표현한다. (이것은 아이콘도 마찬가지)

 

결국, selected, unselected  상태에서 label 색을 정해주기 위해서는 colorTween 이 어떻게 결정되는지를 보면 된다.

 

    switch (_effectiveType) {
      case BottomNavigationBarType.fixed:
        colorTween = ColorTween(
          begin: widget.unselectedItemColor
            ?? bottomTheme.unselectedItemColor
            ?? themeData.unselectedWidgetColor,
          end: widget.selectedItemColor
            ?? bottomTheme.selectedItemColor
            ?? widget.fixedColor
            ?? themeColor,
        );
        break;
      case BottomNavigationBarType.shifting:
        colorTween = ColorTween(
          begin: widget.unselectedItemColor
            ?? bottomTheme.unselectedItemColor
            ?? themeData.colorScheme.surface,
          end: widget.selectedItemColor
            ?? bottomTheme.selectedItemColor
            ?? themeData.colorScheme.surface,
        );
        break;
    }
    
 // bottom_navigation_bar.dart

 

widget 의 selectedItemColor, unselectedItemColor 가 최우선적으로 사용되고,

BottomNavigationBarTheme.of(context) 의 selectedItemColor, unselectedItemColor,

Theme.of(context)의 fixedColor, unselectedWidgetColor 가 다음 우선 순위로 적용된다.

 

이 중 적당한 곳에 필요한 값을 넣어주면 되겠다.