round_tab_indicator.dart 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. // Copyright 2014 The Flutter Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. import 'package:flutter/material.dart';
  5. /// Used with [TabBar.indicator] to draw a horizontal line below the
  6. /// selected tab.
  7. ///
  8. /// The selected tab underline is inset from the tab's boundary by [insets].
  9. /// The [borderSide] defines the line's color and weight.
  10. ///
  11. /// The [TabBar.indicatorSize] property can be used to define the indicator's
  12. /// bounds in terms of its (centered) widget with [TabBarIndicatorSize.label],
  13. /// or the entire tab with [TabBarIndicatorSize.tab].
  14. class RoundUnderlineTabIndicator extends Decoration {
  15. /// Create an underline style selected tab indicator.
  16. ///
  17. /// The [borderSide] and [insets] arguments must not be null.
  18. const RoundUnderlineTabIndicator({
  19. this.borderSide = const BorderSide(width: 2.0, color: Colors.white),
  20. this.insets = EdgeInsets.zero,
  21. this.width = 26.0,
  22. }) : assert(borderSide != null),
  23. assert(insets != null);
  24. /// The color and weight of the horizontal line drawn below the selected tab.
  25. final BorderSide borderSide;
  26. /// Locates the selected tab's underline relative to the tab's boundary.
  27. ///
  28. /// The [TabBar.indicatorSize] property can be used to define the tab
  29. /// indicator's bounds in terms of its (centered) tab widget with
  30. /// [TabBarIndicatorSize.label], or the entire tab with
  31. /// [TabBarIndicatorSize.tab].
  32. final EdgeInsetsGeometry insets;
  33. final double width;
  34. @override
  35. Decoration? lerpFrom(Decoration? a, double t) {
  36. if (a is RoundUnderlineTabIndicator) {
  37. return RoundUnderlineTabIndicator(
  38. borderSide: BorderSide.lerp(a.borderSide, borderSide, t),
  39. insets: EdgeInsetsGeometry.lerp(a.insets, insets, t)!,
  40. );
  41. }
  42. return super.lerpFrom(a, t);
  43. }
  44. @override
  45. Decoration? lerpTo(Decoration? b, double t) {
  46. if (b is RoundUnderlineTabIndicator) {
  47. return RoundUnderlineTabIndicator(
  48. borderSide: BorderSide.lerp(borderSide, b.borderSide, t),
  49. insets: EdgeInsetsGeometry.lerp(insets, b.insets, t)!,
  50. );
  51. }
  52. return super.lerpTo(b, t);
  53. }
  54. @override
  55. BoxPainter createBoxPainter([VoidCallback? onChanged]) {
  56. return _UnderlinePainter(this, onChanged);
  57. }
  58. Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
  59. assert(rect != null);
  60. assert(textDirection != null);
  61. final Rect indicator = insets.resolve(textDirection).deflateRect(rect);
  62. if (width == 0) {
  63. return Rect.fromLTWH(
  64. indicator.left,
  65. indicator.bottom - borderSide.width,
  66. indicator.width,
  67. borderSide.width,
  68. );
  69. } else {
  70. return Rect.fromCenter(center: Offset(rect.center.dx, indicator.bottom - borderSide.width), width: 26, height: borderSide.width);
  71. }
  72. }
  73. @override
  74. Path getClipPath(Rect rect, TextDirection textDirection) {
  75. return Path()..addRect(_indicatorRectFor(rect, textDirection));
  76. }
  77. }
  78. class _UnderlinePainter extends BoxPainter {
  79. _UnderlinePainter(this.decoration, VoidCallback? onChanged)
  80. : assert(decoration != null),
  81. super(onChanged);
  82. final RoundUnderlineTabIndicator decoration;
  83. @override
  84. void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
  85. assert(configuration != null);
  86. assert(configuration.size != null);
  87. final Rect rect = offset & configuration.size!;
  88. final TextDirection textDirection = configuration.textDirection!;
  89. final Rect indicator = decoration._indicatorRectFor(rect, textDirection).deflate(decoration.borderSide.width / 2.0);
  90. final Paint paint = decoration.borderSide.toPaint()..strokeCap = StrokeCap.round;
  91. canvas.drawLine(indicator.bottomLeft, indicator.bottomRight, paint);
  92. }
  93. }