在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'),
)),
);
}
可以看到实现的效果是包裹住了整个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);
}
有时候需要固定下划线的宽高,可以采用下面的写法。
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);
}