Skip to content

Commit 3512c2c

Browse files
authored
Fix divider width in scrollable TabBar for Material 3 and add dividerHeight parameter (flutter#123127)
Fix divider width in scrollable `TabBar` for Material 3 and add `dividerHeight` parameter
1 parent d6287cc commit 3512c2c

File tree

6 files changed

+171
-74
lines changed

6 files changed

+171
-74
lines changed

dev/tools/gen_defaults/lib/tabs_template.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ class _${blockName}PrimaryDefaultsM3 extends TabBarTheme {
2020
late final ColorScheme _colors = Theme.of(context).colorScheme;
2121
late final TextTheme _textTheme = Theme.of(context).textTheme;
2222
23+
@override
24+
double? get dividerHeight => ${tokens['md.comp.primary-navigation-tab.divider.height']};
25+
2326
@override
2427
Color? get dividerColor => ${componentColor("md.comp.primary-navigation-tab.divider")};
2528
@@ -81,6 +84,9 @@ class _${blockName}SecondaryDefaultsM3 extends TabBarTheme {
8184
@override
8285
Color? get dividerColor => ${componentColor("md.comp.secondary-navigation-tab.divider")};
8386
87+
@override
88+
double? get dividerHeight => ${tokens['md.comp.primary-navigation-tab.divider.height']};
89+
8490
@override
8591
Color? get indicatorColor => ${componentColor("md.comp.primary-navigation-tab.active-indicator")};
8692

packages/flutter/lib/src/material/tab_bar_theme.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class TabBarTheme with Diagnosticable {
3232
this.indicatorColor,
3333
this.indicatorSize,
3434
this.dividerColor,
35+
this.dividerHeight,
3536
this.labelColor,
3637
this.labelPadding,
3738
this.labelStyle,
@@ -54,6 +55,9 @@ class TabBarTheme with Diagnosticable {
5455
/// Overrides the default value for [TabBar.dividerColor].
5556
final Color? dividerColor;
5657

58+
/// Overrides the default value for [TabBar.dividerHeight].
59+
final double? dividerHeight;
60+
5761
/// Overrides the default value for [TabBar.labelColor].
5862
///
5963
/// If [labelColor] is a [MaterialStateColor], then the effective color will
@@ -97,6 +101,7 @@ class TabBarTheme with Diagnosticable {
97101
Color? indicatorColor,
98102
TabBarIndicatorSize? indicatorSize,
99103
Color? dividerColor,
104+
double? dividerHeight,
100105
Color? labelColor,
101106
EdgeInsetsGeometry? labelPadding,
102107
TextStyle? labelStyle,
@@ -111,6 +116,7 @@ class TabBarTheme with Diagnosticable {
111116
indicatorColor: indicatorColor ?? this.indicatorColor,
112117
indicatorSize: indicatorSize ?? this.indicatorSize,
113118
dividerColor: dividerColor ?? this.dividerColor,
119+
dividerHeight: dividerHeight ?? this.dividerHeight,
114120
labelColor: labelColor ?? this.labelColor,
115121
labelPadding: labelPadding ?? this.labelPadding,
116122
labelStyle: labelStyle ?? this.labelStyle,
@@ -141,6 +147,7 @@ class TabBarTheme with Diagnosticable {
141147
indicatorColor: Color.lerp(a.indicatorColor, b.indicatorColor, t),
142148
indicatorSize: t < 0.5 ? a.indicatorSize : b.indicatorSize,
143149
dividerColor: Color.lerp(a.dividerColor, b.dividerColor, t),
150+
dividerHeight: t < 0.5 ? a.dividerHeight : b.dividerHeight,
144151
labelColor: Color.lerp(a.labelColor, b.labelColor, t),
145152
labelPadding: EdgeInsetsGeometry.lerp(a.labelPadding, b.labelPadding, t),
146153
labelStyle: TextStyle.lerp(a.labelStyle, b.labelStyle, t),
@@ -158,6 +165,7 @@ class TabBarTheme with Diagnosticable {
158165
indicatorColor,
159166
indicatorSize,
160167
dividerColor,
168+
dividerHeight,
161169
labelColor,
162170
labelPadding,
163171
labelStyle,
@@ -181,6 +189,7 @@ class TabBarTheme with Diagnosticable {
181189
&& other.indicatorColor == indicatorColor
182190
&& other.indicatorSize == indicatorSize
183191
&& other.dividerColor == dividerColor
192+
&& other.dividerHeight == dividerHeight
184193
&& other.labelColor == labelColor
185194
&& other.labelPadding == labelPadding
186195
&& other.labelStyle == labelStyle

packages/flutter/lib/src/material/tab_indicator.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ class _UnderlinePainter extends BoxPainter {
109109
if (borderRadius != null) {
110110
paint = Paint()..color = decoration.borderSide.color;
111111
final Rect indicator = decoration._indicatorRectFor(rect, textDirection)
112-
.inflate(decoration.borderSide.width / 4.0);
112+
.inflate(decoration.borderSide.width / 4.0)
113+
.shift(Offset(0.0, -decoration.borderSide.width / 2.0));
113114
final RRect rrect = RRect.fromRectAndCorners(
114115
indicator,
115116
topLeft: borderRadius!.topLeft,

packages/flutter/lib/src/material/tabs.dart

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'color_scheme.dart';
1515
import 'colors.dart';
1616
import 'constants.dart';
1717
import 'debug.dart';
18+
import 'divider.dart';
1819
import 'ink_well.dart';
1920
import 'material.dart';
2021
import 'material_localizations.dart';
@@ -360,7 +361,6 @@ class _IndicatorPainter extends CustomPainter {
360361
required _IndicatorPainter? old,
361362
required this.indicatorPadding,
362363
required this.labelPaddings,
363-
this.dividerColor,
364364
}) : super(repaint: controller.animation) {
365365
if (old != null) {
366366
saveTabOffsets(old._currentTabOffsets, old._currentTextDirection);
@@ -372,7 +372,6 @@ class _IndicatorPainter extends CustomPainter {
372372
final TabBarIndicatorSize? indicatorSize;
373373
final EdgeInsetsGeometry indicatorPadding;
374374
final List<GlobalKey> tabKeys;
375-
final Color? dividerColor;
376375
final List<EdgeInsetsGeometry> labelPaddings;
377376

378377
// _currentTabOffsets and _currentTextDirection are set each time TabBar
@@ -465,10 +464,6 @@ class _IndicatorPainter extends CustomPainter {
465464
size: _currentRect!.size,
466465
textDirection: _currentTextDirection,
467466
);
468-
if (dividerColor != null) {
469-
final Paint dividerPaint = Paint()..color = dividerColor!..strokeWidth = 1;
470-
canvas.drawLine(Offset(0, size.height), Offset(size.width, size.height), dividerPaint);
471-
}
472467
_painter!.paint(canvas, _currentRect!.topLeft, configuration);
473468
}
474469

@@ -682,6 +677,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
682677
this.indicator,
683678
this.indicatorSize,
684679
this.dividerColor,
680+
this.dividerHeight,
685681
this.labelColor,
686682
this.labelStyle,
687683
this.labelPadding,
@@ -731,6 +727,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
731727
this.indicator,
732728
this.indicatorSize,
733729
this.dividerColor,
730+
this.dividerHeight,
734731
this.labelColor,
735732
this.labelStyle,
736733
this.labelPadding,
@@ -849,6 +846,13 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
849846
/// [ColorScheme.surfaceVariant] will be used, otherwise divider will not be drawn.
850847
final Color? dividerColor;
851848

849+
/// The height of the divider.
850+
///
851+
/// If null and [ThemeData.useMaterial3] is true, [TabBarTheme.dividerHeight]
852+
/// is used. If that is null and [ThemeData.useMaterial3] is true, 1.0 will be used.
853+
/// Otherwise divider will not be drawn.
854+
final double? dividerHeight;
855+
852856
/// The color of selected tab labels.
853857
///
854858
/// If null, then [TabBarTheme.labelColor] is used. If that is also null and
@@ -1096,7 +1100,7 @@ class _TabBarState extends State<TabBar> {
10961100
}
10971101
}
10981102

1099-
Decoration _getIndicator() {
1103+
Decoration _getIndicator(TabBarIndicatorSize indicatorSize) {
11001104
final ThemeData theme = Theme.of(context);
11011105
final TabBarTheme tabBarTheme = TabBarTheme.of(context);
11021106

@@ -1130,17 +1134,24 @@ class _TabBarState extends State<TabBar> {
11301134
color = Colors.white;
11311135
}
11321136

1133-
return UnderlineTabIndicator(
1134-
borderRadius: theme.useMaterial3 && widget._isPrimary
1137+
if (theme.useMaterial3 && widget._isPrimary && indicatorSize == TabBarIndicatorSize.label) {
1138+
return UnderlineTabIndicator(
1139+
borderRadius: const BorderRadius.only(
1140+
topLeft: Radius.circular(3.0),
1141+
topRight: Radius.circular(3.0),
1142+
),
1143+
borderSide: BorderSide(
11351144
// TODO(tahatesser): Make sure this value matches Material 3 Tabs spec
11361145
// when `preferredSize`and `indicatorWeight` are updated to support Material 3
11371146
// https://m3.material.io/components/tabs/specs#149a189f-9039-4195-99da-15c205d20e30,
11381147
// https://github.com/flutter/flutter/issues/116136
1139-
? const BorderRadius.only(
1140-
topLeft: Radius.circular(3.0),
1141-
topRight: Radius.circular(3.0),
1142-
)
1143-
: null,
1148+
width: widget.indicatorWeight,
1149+
color: color,
1150+
),
1151+
);
1152+
}
1153+
1154+
return UnderlineTabIndicator(
11441155
borderSide: BorderSide(
11451156
width: widget.indicatorWeight,
11461157
color: color,
@@ -1185,17 +1196,18 @@ class _TabBarState extends State<TabBar> {
11851196
}
11861197

11871198
void _initIndicatorPainter() {
1188-
final ThemeData theme = Theme.of(context);
11891199
final TabBarTheme tabBarTheme = TabBarTheme.of(context);
1200+
final TabBarIndicatorSize indicatorSize = widget.indicatorSize
1201+
?? tabBarTheme.indicatorSize
1202+
?? _defaults.indicatorSize!;
11901203

11911204
_indicatorPainter = !_controllerIsValid ? null : _IndicatorPainter(
11921205
controller: _controller!,
1193-
indicator: _getIndicator(),
1194-
indicatorSize: widget.indicatorSize ?? tabBarTheme.indicatorSize ?? _defaults.indicatorSize!,
1206+
indicator: _getIndicator(indicatorSize),
1207+
indicatorSize: indicatorSize,
11951208
indicatorPadding: widget.indicatorPadding,
11961209
tabKeys: _tabKeys,
11971210
old: _indicatorPainter,
1198-
dividerColor: theme.useMaterial3 ? widget.dividerColor ?? tabBarTheme.dividerColor ?? _defaults.dividerColor : null,
11991211
labelPaddings: _labelPaddings,
12001212
);
12011213
}
@@ -1390,6 +1402,7 @@ class _TabBarState extends State<TabBar> {
13901402
);
13911403
}
13921404

1405+
final ThemeData theme = Theme.of(context);
13931406
final TabBarTheme tabBarTheme = TabBarTheme.of(context);
13941407

13951408
final List<Widget> wrappedTabs = List<Widget>.generate(widget.tabs.length, (int index) {
@@ -1535,6 +1548,24 @@ class _TabBarState extends State<TabBar> {
15351548
);
15361549
}
15371550

1551+
if (theme.useMaterial3) {
1552+
tabBar = Stack(
1553+
alignment: Alignment.center,
1554+
children: <Widget>[
1555+
Container(
1556+
height: widget.preferredSize.height,
1557+
alignment: Alignment.bottomCenter,
1558+
child: Divider(
1559+
height: 0,
1560+
thickness: widget.dividerHeight ?? tabBarTheme.dividerHeight ?? _defaults.dividerHeight,
1561+
color: widget.dividerColor ?? tabBarTheme.dividerColor ?? _defaults.dividerColor,
1562+
),
1563+
),
1564+
tabBar,
1565+
],
1566+
);
1567+
}
1568+
15381569
return tabBar;
15391570
}
15401571
}
@@ -2068,6 +2099,9 @@ class _TabsPrimaryDefaultsM3 extends TabBarTheme {
20682099
late final ColorScheme _colors = Theme.of(context).colorScheme;
20692100
late final TextTheme _textTheme = Theme.of(context).textTheme;
20702101

2102+
@override
2103+
double? get dividerHeight => 1.0;
2104+
20712105
@override
20722106
Color? get dividerColor => _colors.surfaceVariant;
20732107

@@ -2129,6 +2163,9 @@ class _TabsSecondaryDefaultsM3 extends TabBarTheme {
21292163
@override
21302164
Color? get dividerColor => _colors.surfaceVariant;
21312165

2166+
@override
2167+
double? get dividerHeight => 1.0;
2168+
21322169
@override
21332170
Color? get indicatorColor => _colors.primary;
21342171

0 commit comments

Comments
 (0)