custom_tab_indicator.dart 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. import 'dart:ui';
  2. import 'package:flutter/material.dart';
  3. /// 自定义Tab的指示器
  4. class CustomTabIndicator extends Decoration {
  5. const CustomTabIndicator({
  6. // 指示器的宽度
  7. this.width = 20,
  8. // 指示器的形状,默认为圆角
  9. this.strokeCap = StrokeCap.round,
  10. // 指示器的高度和纯颜色,渐变色优先于这里的纯颜色
  11. this.borderSide = const BorderSide(width: 2.0, color: Colors.white),
  12. // 指示器距离Tab的外边距
  13. this.insets = EdgeInsets.zero,
  14. // 渐变色
  15. this.gradient,
  16. });
  17. final BorderSide borderSide;
  18. final EdgeInsetsGeometry insets;
  19. /// 新增属性:控制器宽度
  20. final double width;
  21. /// 新增属性:控制器边角形状
  22. final StrokeCap strokeCap;
  23. /// 新增属性:渐变色
  24. final Gradient? gradient;
  25. @override
  26. Decoration? lerpFrom(Decoration? a, double t) {
  27. if (a is CustomTabIndicator) {
  28. return CustomTabIndicator(
  29. borderSide: BorderSide.lerp(a.borderSide, borderSide, t),
  30. insets: EdgeInsetsGeometry.lerp(a.insets, insets, t)!,
  31. width: lerpDouble(a.width, width, t)!,
  32. // 渐变插值
  33. gradient: Gradient.lerp(a.gradient, gradient, t),
  34. );
  35. }
  36. return super.lerpFrom(a, t);
  37. }
  38. @override
  39. Decoration? lerpTo(Decoration? b, double t) {
  40. if (b is CustomTabIndicator) {
  41. return CustomTabIndicator(
  42. borderSide: BorderSide.lerp(borderSide, b.borderSide, t),
  43. insets: EdgeInsetsGeometry.lerp(insets, b.insets, t)!,
  44. width: lerpDouble(width, b.width, t)!,
  45. // 渐变插值
  46. gradient: Gradient.lerp(gradient, b.gradient, t),
  47. );
  48. }
  49. return super.lerpTo(b, t);
  50. }
  51. @override
  52. BoxPainter createBoxPainter([VoidCallback? onChanged]) {
  53. return _UnderlinePainter(this, onChanged);
  54. }
  55. /// 决定控制器宽度的方法
  56. Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
  57. final Rect indicator = insets.resolve(textDirection).deflateRect(rect);
  58. double cw = (indicator.left + indicator.right) / 2;
  59. // 指示器居中
  60. return Rect.fromLTWH(
  61. cw - width / 2,
  62. indicator.bottom - borderSide.width,
  63. width,
  64. borderSide.width,
  65. );
  66. }
  67. @override
  68. Path getClipPath(Rect rect, TextDirection textDirection) {
  69. return Path()..addRect(_indicatorRectFor(rect, textDirection));
  70. }
  71. }
  72. class _UnderlinePainter extends BoxPainter {
  73. _UnderlinePainter(this.decoration, VoidCallback? onChanged)
  74. : super(onChanged);
  75. final CustomTabIndicator decoration;
  76. @override
  77. void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
  78. final Rect rect = offset & configuration.size!;
  79. final TextDirection textDirection = configuration.textDirection!;
  80. final Rect indicator = decoration
  81. ._indicatorRectFor(rect, textDirection)
  82. .deflate(decoration.borderSide.width / 2.0);
  83. final Paint paint =
  84. Paint()
  85. ..strokeCap = decoration.strokeCap
  86. ..strokeWidth = decoration.borderSide.width;
  87. // 优先使用渐变色
  88. if (decoration.gradient != null) {
  89. final gradient = decoration.gradient!;
  90. paint.shader = gradient.createShader(
  91. Rect.fromPoints(indicator.bottomLeft, indicator.bottomRight),
  92. );
  93. } else {
  94. // 使用纯色
  95. paint.color = decoration.borderSide.color;
  96. }
  97. canvas.drawLine(indicator.bottomLeft, indicator.bottomRight, paint);
  98. }
  99. }