Skip to content

Commit 24efb55

Browse files
author
Hans Muller
authored
Generalized TabBar selected tab indicator (flutter#14741)
1 parent 21c514f commit 24efb55

File tree

6 files changed

+578
-181
lines changed

6 files changed

+578
-181
lines changed

packages/flutter/lib/material.dart

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export 'src/material/stepper.dart';
8181
export 'src/material/switch.dart';
8282
export 'src/material/switch_list_tile.dart';
8383
export 'src/material/tab_controller.dart';
84+
export 'src/material/tab_indicator.dart';
8485
export 'src/material/tabs.dart';
8586
export 'src/material/text_field.dart';
8687
export 'src/material/text_form_field.dart';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2018 The Chromium 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+
5+
import 'package:flutter/widgets.dart';
6+
7+
import 'colors.dart';
8+
9+
/// Used with [TabBar.indicator] to draw a horizontal line below the
10+
/// selected tab.
11+
///
12+
/// The selected tab underline is inset from the tab's boundary by [insets].
13+
/// The [borderSide] defines the line's color and weight.
14+
///
15+
/// The [TabBar.indicatorSize] property can be used to define the indicator's
16+
/// bounds in terms of its (centered) widget with [TabIndicatorSize.label],
17+
/// or the entire tab with [TabIndicatorSize.tab].
18+
class UnderlineTabIndicator extends Decoration {
19+
/// Create an underline style selected tab indicator.
20+
///
21+
/// The [borderSide] and [insets] arguments must not be null.
22+
const UnderlineTabIndicator({
23+
this.borderSide: const BorderSide(width: 2.0, color: Colors.white),
24+
this.insets: EdgeInsets.zero,
25+
}) : assert(borderSide != null), assert(insets != null);
26+
27+
/// The color and weight of the horizontal line drawn below the selected tab.
28+
final BorderSide borderSide;
29+
30+
/// Locates the selected tab's underline relative to the tab's boundary.
31+
///
32+
/// The [TabBar.indicatorSize] property can be used to define the
33+
/// tab indicator's bounds in terms of its (centered) tab widget with
34+
/// [TabIndicatorSize.label], or the entire tab with [TabIndicatorSize.tab].
35+
final EdgeInsetsGeometry insets;
36+
37+
@override
38+
Decoration lerpFrom(Decoration a, double t) {
39+
if (a is UnderlineTabIndicator) {
40+
return new UnderlineTabIndicator(
41+
borderSide: BorderSide.lerp(a.borderSide, borderSide, t),
42+
insets: EdgeInsetsGeometry.lerp(a.insets, insets, t),
43+
);
44+
}
45+
return super.lerpFrom(a, t);
46+
}
47+
48+
@override
49+
Decoration lerpTo(Decoration b, double t) {
50+
if (b is UnderlineTabIndicator) {
51+
return new UnderlineTabIndicator(
52+
borderSide: BorderSide.lerp(borderSide, b.borderSide, t),
53+
insets: EdgeInsetsGeometry.lerp(insets, b.insets, t),
54+
);
55+
}
56+
return super.lerpTo(b, t);
57+
}
58+
59+
@override
60+
_UnderlinePainter createBoxPainter([VoidCallback onChanged]) {
61+
return new _UnderlinePainter(this, onChanged);
62+
}
63+
}
64+
65+
class _UnderlinePainter extends BoxPainter {
66+
_UnderlinePainter(this.decoration, VoidCallback onChanged)
67+
: assert(decoration != null), super(onChanged);
68+
69+
final UnderlineTabIndicator decoration;
70+
71+
BorderSide get borderSide => decoration.borderSide;
72+
EdgeInsetsGeometry get insets => decoration.insets;
73+
74+
Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
75+
assert(rect != null);
76+
assert(textDirection != null);
77+
final Rect indicator = insets.resolve(textDirection).deflateRect(rect);
78+
return new Rect.fromLTWH(
79+
indicator.left,
80+
indicator.bottom - borderSide.width,
81+
indicator.width,
82+
borderSide.width,
83+
);
84+
}
85+
86+
@override
87+
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
88+
assert(configuration != null);
89+
assert(configuration.size != null);
90+
final Rect rect = offset & configuration.size;
91+
final TextDirection textDirection = configuration.textDirection;
92+
final Rect indicator = _indicatorRectFor(rect, textDirection).deflate(borderSide.width / 2.0);
93+
canvas.drawLine(indicator.bottomLeft, indicator.bottomRight, borderSide.toPaint());
94+
}
95+
}

0 commit comments

Comments
 (0)