携手创作,一起生长!这是我参与「日新计划 8 月更文挑战」的第1天,点击检查活动概况

需求布景

首页开发需求要求完成每个频道具备不同主题色风格,因而需求完成TabBar每个Tab具备自己主题色。Flutter官方供给TabBar组件只支撑设置选中和非选中条件标签色彩并不支撑装备不同更多不同装备色,TabBar组件装备项为labelColorunselectedLabelColor两者。因而若需求自定义完成支撑装备主题色TabBar组件。

【Flutter】实现自定义TabBar主题色配置

改造完成详解

TabBar切换字体抖动问题解决

这在此之前文章中有提到过解决方案,主要完成逻辑是将原先切换动画替换为缩放完成,规避了动画完成出现的抖动问题。

解决方案

TabBar切换字体主题色完成

  1. TabBar入参供给每个Tab的色彩装备: final List labelColors;
  2. 找到TabBar切换逻辑代码【_TabBarState】:【_buildStyledTab】

_buildStyledTab中TabStyle办法担任构建每个Tab款式,调整该办法添加构建当时TabStylePositioncurrentPosition,分别为对应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,
    );
}
  1. 调整_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);
  1. 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主题色之后也希望指示器支撑跟从主题色改变。

  1. 自定义指示器承继Decoration添加三个入参TabControllerList<Color>width
  2. _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;
  1. _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,
    );

终究效果

具体代码看这里