【Flutter】实现自定义TabBar主题色配置
需求背景
首页开发需求要求实现每个频道具备不同主题色风格,因此需要实现TabBar
每个Tab
具备自己主题色。Flutter
官方提供TabBar
组件只支持设置选中和非选中条件标签颜色并不支持配置不同更多不同配置色,TabBar
组件配置项为labelColor
和unselectedLabelColor
两者。因此若需要自定义实现支持配置主题色TabBar
组件。
改造实现详解
TabBar切换字体抖动问题解决
这在此之前文章中有提到过解决方案,主要实现逻辑是将原先切换动画替换为缩放实现,规避了动画实现出现的抖动问题。
TabBar切换字体主题色实现
TabBar
入参提供每个Tab的颜色配置: final List labelColors;- 找到
TabBar
切换逻辑代码【_TabBarState】:【_buildStyledTab】
_buildStyledTab中TabStyle
方法负责构建每个Tab
样式,调整该方法增加构建当前TabStyle
的Position
和currentPosition
,分别为对应Tab
的样式和当前选中Tab
的样式
Widget _buildStyledTab(Widget child,int position,int currentPosition, bool selected, Animation<double> animation,TabController controller) {
Color labelColor;
Color unselectedLabelColor;
labelColor = widget.labelColors[position];
unselectedLabelColor = widget.labelColors[currentPosition];
return _TabStyle(
animation: animation,
selected: selected,
labelColors: widget.labelColors,
labelColor: labelColor,
unselectedLabelColor: unselectedLabelColor,
labelStyle: widget.labelStyle,
unselectedLabelStyle: widget.unselectedLabelStyle,
tabController:controller,
child: child,
);
}
- 调整_TabStyle方法内部逻辑
增加以下代码逻辑通过TabController
获取当前选中Tab
定位并且增加渐变透明度调整
// 判断是否是临近的下一个Tab
bool isNext = false;
// 透明度不好计算呀
double opacity = 0.5;
// 当前选中的Tab
int selectedValue = tabController.index;
selectedColor = labelColors[selectedValue];
// 当前偏移方向
if (tabController.offset > 0) {
unselectedColor = labelColors[selectedValue + 1];
isNext = false;
} else if (tabController.offset < 0) {
isNext = true;
unselectedColor = labelColors[selectedValue - 1];
} else {
unselectedColor = selectedColor;
}
if (unselectedColor != Color(0xFF333333)) {
opacity = 0.9;
}
final Color color = selected
? Color.lerp(selectedColor, unselectedColor.withOpacity(opacity),
colorAnimation.value)
: unBuild
? Color.lerp(selectedColor.withOpacity(opacity),
unselectedColor.withOpacity(opacity), colorAnimation.value)
: Color.lerp(
selectedColor.withOpacity(opacity),
unselectedColor.withOpacity(isNext ? 1 : opacity),
colorAnimation.value);
- 在
CustomPaint
组件同样也需要增加选中色值设置
Color labelColor;
Color unselectedLabelColor;
labelColor = widget.labelColors[_currentIndex];
unselectedLabelColor = widget.labelColors[_currentIndex];
final Animation<double> animation = _ChangeAnimation(_controller);
Widget magicTabBar = CustomPaint(
painter: _indicatorPainter,
child: _TabStyle(
animation: animation,
selected: false,
unBuild: true,
labelColor: labelColor,
unselectedLabelColor: unselectedLabelColor,
labelColors: widget.labelColors,
labelStyle: widget.labelStyle,
unselectedLabelStyle: widget.unselectedLabelStyle,
tabController: widget.controller,
child: _TabLabelBar(
onPerformLayout: _saveTabOffsets,
children: wrappedTabs,
),
),
);
TabBar指示器自定义
官方提供TabBar
的选中指示器长度是跟随Tab
宽度不能做到固定宽度,且当改造TabBar
主题色之后也期望指示器支持跟随主题色变化。
- 自定义指示器继承
Decoration
增加三个入参TabController
、List<Color>
、width
。 - _UnderlinePainter增加当前选中
Tab
逻辑来确定主题色选择。
double page = 0;
int realPage = 0;
page = pageController.index + pageController.offset ?? 0;
realPage = pageController.index + pageController.offset?.floor() ?? 0;
double opacity = 1 - (page - realPage).abs();
Color thisColor = labelColors[realPage];
thisColor = thisColor;
Color nextColor = labelColors[
realPage + 1 < labelColors.length ? realPage + 1 : realPage];
nextColor = nextColor;
- _indicatorRectFor方法修改指示器宽度方法,计算出Tab的中心位置再根据设置宽度绘制最终偏移量位置信息。
final Rect indicator = insets.resolve(textDirection).deflateRect(rect);
double midValue = (indicator.right - indicator.left) / 2 + indicator.left;
return Rect.fromLTWH(
midValue - width / 2,
indicator.bottom - borderSide.width,
width,
borderSide.width,
);
最终效果
作者:JulyYu
链接:https://juejin.cn/post/7133880100914724871
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。