Flutter 自定义 TabBar 的 indicator

3,845 阅读2分钟

前段时间做了一个需求,里面有一个 UI 是实现一个炫酷的 TabBar 切换,但是 Flutter 提供的效果有限。经过自己的摸索最终是实现了,所以个人觉得有必要记录一下。也给需要的看官朋友提供一个思路。

UI 想要的是这样(大致是这个方向,不止这么简单, 还有一些花里胡哨的效果)

RPReplay_Final1634096552.gif

但我一把撸的是这样

RPReplay_Final1634033026 2.gif

所以只能模改一番官方的 UnderlineTabIndicator,详细操作看我怎么抄作业~

抄作业

1. 找答案

TabBar() 里面有一个属性 indicator,可以支持自定义指示器

image.png

2.抄

  • 创建一个文件,命名 rectangle_indicator.dart(命名随意)
  • 点进 UnderlineTabIndicator,全选、Copy
  • 内容粘贴到 rectangle_indicator.dart
  • 然后把相关的类名改成 RectangleIndicator
  • 处理一下类名不对应的报错信息

3.改

image.png 根据需求修改 indicator(位置、尺寸)paint(样式)即可

为了更加灵活一点,可以公开一些配置,比如:

// 圆角
final double radius;
// 周围间距
final EdgeInsets padding;
// 颜色
final Color rectangleColor;
// 阴影颜色
final Color? shadowColor;
const RectangleIndicator({
  this.borderSide = const BorderSide(width: 2.0, color: Colors.white),
  this.insets = EdgeInsets.zero,
  this.radius = 4.0,
  this.padding = const EdgeInsets.all(2.0),
  this.rectangleColor = Colors.redAccent,
  this.shadowColor,
})  : assert(borderSide != null),
      assert(insets != null);
// 绘制 indicator
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
  assert(configuration != null);
  assert(configuration.size != null);
  final Rect rect = offset & configuration.size!;
  final TextDirection textDirection = configuration.textDirection!;
  // 计算 indicator 的 rect
  final Rect indicator = decoration._indicatorRectFor(rect, textDirection).deflate(decoration.borderSide.width / 2.0);
  // 定义绘制的样式
  final RRect rrect = RRect.fromRectAndRadius(indicator, Radius.circular(decoration.radius));
  final Paint paint = decoration.borderSide.toPaint()
    ..style = PaintingStyle.fill
    ..color = decoration.rectangleColor;
  final Path path = Path()..addRRect(rrect.shift(Offset(1, 1)));
  // 绘制阴影
  if (decoration.shadowColor != null) {
    canvas..drawShadow(path, decoration.shadowColor!, 4, false);
  }
  // 绘制圆角矩形
  canvas..drawRRect(rrect, paint);
}
// 计算 indicator 的 rect
Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
  assert(rect != null);
  assert(textDirection != null);
  final Rect indicator = insets.resolve(textDirection).deflateRect(rect);
  return Rect.fromLTWH(
    indicator.left + padding.left,
    padding.top,
    indicator.width - padding.left - padding.right,
    indicator.height - padding.top - padding.bottom,
  );
}

rectangle_indicator.dart 的修改就到此为止了

使用起来也很简单

image.png

**3.交卷 **

Demo

本文只是作为日常开发的记录,所以只记录了简单的方向和思路。不同的需求也应又不同的效果,最后还是需要细细雕刻。