Flutter TabBar渐变色Indicator

1,306 阅读1分钟

在Flutter中TabBar的indicator是一个Decoration对象。所以要自定义indicator之前要先了解Decoration对象。

Decoration的子类

  • BoxDecoration:实现边框、圆角、阴影、形状、渐变、背景图像
  • ShapeDecoration:实现四个边分别指定颜色和宽度、底部线、矩形边色、圆形边色、体育场(竖向椭圆)、 角形(八边角)边色
  • FlutterLogoDecoration:实现Flutter图片
  • UnderlineTabindicator:下划线,TabBar的indicator默认样式。

ShapeDecoration的构造方法。

const ShapeDecoration({
  this.color,
  this.image,
  this.gradient,//渐变色
  this.shadows,
  required this.shape,
})

BoxDecoration的构造方法。

const BoxDecoration({
  this.color,
  this.image,
  this.border,
  this.borderRadius,
  this.boxShadow,
  this.gradient,//渐变色
  this.backgroundBlendMode,
  this.shape = BoxShape.rectangle,
})

当要实现渐变色的Indicator,可以采用ShapeDecoration或BoxDecoration然后指定渐变色。

Demo

@override
Widget build(BuildContext context) {
  return DefaultTabController(
    length: 3,
    child: Scaffold(
        appBar: AppBar(
          title: const Text('appbarTitle'),
          bottom: TabBar(
            indicator: ShapeDecoration(
              gradient: const LinearGradient(
                colors: [
                  Colors.red,
                  Colors.yellow,
                ],
              ),
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(20),
              ),
            ),
            tabs:
                ['page1', 'page2', 'page3'].map((e) => Tab(text: e)).toList(),
          ),
        ),
        body: const Center(
          child: Text('hello world'),
        )),
  );
}

image.png

可以看到实现的效果是包裹住了整个Tab,如果想和TabBar的默认indicator一样是下划线的样式呢?就需要将UnderlineTabindicator和ShapeDecoration组合起来。

自定义Indicator

在阅读TabBar源码发现,indicator是通过getClipPath方法来获取的宽度,所以我们保留ShapeDecoration的绘制而重写getClipPath方法来处理宽度。

@override
Path getClipPath(Rect rect, TextDirection textDirection) {
  return Path()..addRect(_indicatorRectFor(rect, textDirection));
}
Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
  assert(rect != null);
  assert(textDirection != null);
  final Rect indicator = insets.resolve(textDirection).deflateRect(rect);
  ///希望的下划线高度
  double wantH = 6;
  return Rect.fromLTWH(
      indicator.left, indicator.bottom - wantH, rect.width, wantH);
}

image.png

有时候需要固定下划线的宽高,可以采用下面的写法。

Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
  assert(rect != null);
  assert(textDirection != null);
  final Rect indicator = insets.resolve(textDirection).deflateRect(rect);
  //希望的下划线宽高
  Size s = size ?? const Size(20, 6);
  double wantW = s.width;
  double wantH = s.height;
  //取中间坐标,计算出偏移量,不然会跟着移动
  double cw = (indicator.left + indicator.right) / 2;
  return Rect.fromLTWH(
      cw - wantW / 2, indicator.bottom - wantH, wantW, wantH);
}

image.png