携手创作,一起生长!这是我参与「日新计划 8 月更文挑战」的第1天,点击检查活动概况
需求布景
首页开发需求要求完成每个频道具备不同主题色风格,因而需求完成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,
);
终究效果
具体代码看这里