flutter 自定义TabBar +自定义Indicator

主要是顶部TabBar+TabView的实现,左右滑动或者点击切换。先上效果图

给每个Tab有个初始的背景颜色,Indicator是粉色的框

首先初始Tab数据,构建初始背景颜色的时候,用到了颜色混合BlendMode,详情可以百度,为了选择时indicator能上色

//用于监听Tab菜单切换
  TabController _tabController;
  //Tab 标签
  List<Widget> _tabs;

_getTabData(){
    _tabs = [];
    int day = serverTime.day;
    int month = serverTime.month;

    for(int i = 0;i<7;i++){
      tabs.add(_getTabText(month, day++));
    }
  }

  //获取tab控件
  Widget _getTabText(int month,int day){
    String tag = "";
    if(( month == DateTime.now().month )&& (day == DateTime.now().day)){
      tag = "今天";
    }else if(( month == DateTime.now().month )&&(day-1) == DateTime.now().day){
      tag = "明天";
    }else{
      tag = "${month.toString()}月${day.toString()}日";
    }
    return  Container(
      child: Text(tag),
      decoration: BoxDecoration(
          color: white_fff4f4f4,
          backgroundBlendMode: BlendMode.luminosity,
          borderRadius: BorderRadius.all(Radius.circular(14.75)),
      ),
      padding: EdgeInsets.only(top: 4,bottom: 4, left: 18,right: 18),
    );
  }

构建TabBar

_buildTabBar(){
 return Container(
      margin: EdgeInsets.only(left: 12),
      child: TabBar(
        isScrollable: true,
        labelPadding: EdgeInsets.only(left:8,right: 8),
        indicator: CustomRRecTabIndicator(
            radius: 14.75,
            color: Color(0xffffe7e7),
        ),
        indicatorSize: TabBarIndicatorSize.label,
        indicatorWeight: 0,
        labelColor: Color(0xffef3454),
        labelStyle: TextStyle(fontSize:15,color: Color(0xffef3454),fontWeight: FontWeight.w700                 
       ),
        unselectedLabelColor:Color(0xffcccccc),
        tabs: _tabs,
        controller: _tabController,
      ),
    );
}

自定义的Indicator,主要是复制官方的Indicator代码,然后稍稍修改,为了修改宽度,圆角

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class CustomRRecTabIndicator extends Decoration{
  /// Create an underline style selected tab indicator.
  ///
  /// The [borderSide] and [insets] arguments must not be null.
  const CustomRRecTabIndicator({
    this.borderSide = const BorderSide(width: 2.0, color: Colors.white),
    this.insets = EdgeInsets.zero,
    this.width,
    this.radius,
    this.color
  }) : assert(borderSide != null),
        assert(insets != null);

  /// The color and weight of the horizontal line drawn below the selected tab.
  final BorderSide borderSide;

  final double radius;

  final double width;

  final Color color;
  /// Locates the selected tab's underline relative to the tab's boundary.
  ///
  /// The [TabBar.indicatorSize] property can be used to define the
  /// tab indicator's bounds in terms of its (centered) tab widget with
  /// [TabIndicatorSize.label], or the entire tab with [TabIndicatorSize.tab].
  final EdgeInsetsGeometry insets;

  @override
  Decoration lerpFrom(Decoration a, double t) {
    if (a is CustomRRecTabIndicator) {
      return CustomRRecTabIndicator(
        borderSide: BorderSide.lerp(a.borderSide, borderSide, t),
        insets: EdgeInsetsGeometry.lerp(a.insets, insets, t),
      );
    }
    return super.lerpFrom(a, t);
  }

  @override
  Decoration lerpTo(Decoration b, double t) {
    if (b is CustomRRecTabIndicator) {
      return CustomRRecTabIndicator(
        borderSide: BorderSide.lerp(borderSide, b.borderSide, t),
        insets: EdgeInsetsGeometry.lerp(insets, b.insets, t),
      );
    }
    return super.lerpTo(b, t);
  }

  @override
  _UnderlinePainter createBoxPainter([ VoidCallback onChanged ]) {
    return _UnderlinePainter(this, onChanged);
  }
}

class _UnderlinePainter extends BoxPainter {
  _UnderlinePainter(this.decoration, VoidCallback onChanged)
      : assert(decoration != null),
        super(onChanged);

  final CustomRRecTabIndicator decoration;

  BorderSide get borderSide => decoration.borderSide;
  EdgeInsetsGeometry get insets => decoration.insets;

  Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
    assert(rect != null);
    assert(textDirection != null);
    final Rect indicator = insets.resolve(textDirection).deflateRect(rect);


    //取中间坐标
    double cw = (indicator.left + indicator.right) / 2;
    return Rect.fromLTWH(cw - decoration.width / 2,
        indicator.bottom - borderSide.width, decoration.width, borderSide.width);
  }

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
    assert(configuration != null);
    assert(configuration.size != null);
    final Rect rect = offset & configuration.size;
    final RRect rRect = RRect.fromRectAndRadius(rect,Radius.circular(decoration.radius));
    canvas.drawRRect(rRect,Paint()..style =PaintingStyle.fill
                                  ..color = decoration.color);



  }

}

TabViewBody就略了,毕竟主要是想讲自定义TabBar


版权声明:本文为BFELFISH原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。