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 가 다음 우선 순위로 적용된다.
이 중 적당한 곳에 필요한 값을 넣어주면 되겠다.