@@ -387,6 +387,39 @@ double _indexChangeProgress(TabController controller) {
387
387
return (controllerValue - currentIndex).abs () / (currentIndex - previousIndex).abs ();
388
388
}
389
389
390
+ class _DividerPainter extends CustomPainter {
391
+ _DividerPainter ({
392
+ required this .dividerColor,
393
+ required this .dividerHeight,
394
+ });
395
+
396
+ final Color dividerColor;
397
+ final double dividerHeight;
398
+
399
+ @override
400
+ void paint (Canvas canvas, Size size) {
401
+ if (dividerHeight <= 0.0 ) {
402
+ return ;
403
+ }
404
+
405
+ final Paint paint = Paint ()
406
+ ..color = dividerColor
407
+ ..strokeWidth = dividerHeight;
408
+
409
+ canvas.drawLine (
410
+ Offset (0 , size.height - (paint.strokeWidth / 2 )),
411
+ Offset (size.width, size.height - (paint.strokeWidth / 2 )),
412
+ paint,
413
+ );
414
+ }
415
+
416
+ @override
417
+ bool shouldRepaint (_DividerPainter oldDelegate) {
418
+ return oldDelegate.dividerColor != dividerColor
419
+ || oldDelegate.dividerHeight != dividerHeight;
420
+ }
421
+ }
422
+
390
423
class _IndicatorPainter extends CustomPainter {
391
424
_IndicatorPainter ({
392
425
required this .controller,
@@ -397,6 +430,8 @@ class _IndicatorPainter extends CustomPainter {
397
430
required this .indicatorPadding,
398
431
required this .labelPaddings,
399
432
this .dividerColor,
433
+ this .dividerHeight,
434
+ required this .showDivider,
400
435
}) : super (repaint: controller.animation) {
401
436
if (old != null ) {
402
437
saveTabOffsets (old._currentTabOffsets, old._currentTextDirection);
@@ -408,8 +443,10 @@ class _IndicatorPainter extends CustomPainter {
408
443
final TabBarIndicatorSize ? indicatorSize;
409
444
final EdgeInsetsGeometry indicatorPadding;
410
445
final List <GlobalKey > tabKeys;
411
- final Color ? dividerColor;
412
446
final List <EdgeInsetsGeometry > labelPaddings;
447
+ final Color ? dividerColor;
448
+ final double ? dividerHeight;
449
+ final bool showDivider;
413
450
414
451
// _currentTabOffsets and _currentTextDirection are set each time TabBar
415
452
// layout is completed. These values can be null when TabBar contains no
@@ -501,9 +538,11 @@ class _IndicatorPainter extends CustomPainter {
501
538
size: _currentRect! .size,
502
539
textDirection: _currentTextDirection,
503
540
);
504
- if (dividerColor != null ) {
505
- final Paint dividerPaint = Paint ()..color = dividerColor! ..strokeWidth = 1 ;
506
- canvas.drawLine (Offset (0 , size.height), Offset (size.width, size.height), dividerPaint);
541
+ if (showDivider && dividerHeight ! > 0 ) {
542
+ final Paint dividerPaint = Paint ()..color = dividerColor! ..strokeWidth = dividerHeight! ;
543
+ final Offset dividerP1 = Offset (0 , size.height - (dividerPaint.strokeWidth / 2 ));
544
+ final Offset dividerP2 = Offset (size.width, size.height - (dividerPaint.strokeWidth / 2 ));
545
+ canvas.drawLine (dividerP1, dividerP2, dividerPaint);
507
546
}
508
547
_painter! .paint (canvas, _currentRect! .topLeft, configuration);
509
548
}
@@ -718,6 +757,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
718
757
this .indicator,
719
758
this .indicatorSize,
720
759
this .dividerColor,
760
+ this .dividerHeight,
721
761
this .labelColor,
722
762
this .labelStyle,
723
763
this .labelPadding,
@@ -768,6 +808,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
768
808
this .indicator,
769
809
this .indicatorSize,
770
810
this .dividerColor,
811
+ this .dividerHeight,
771
812
this .labelColor,
772
813
this .labelStyle,
773
814
this .labelPadding,
@@ -895,6 +936,13 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
895
936
/// [ColorScheme.surfaceVariant] will be used, otherwise divider will not be drawn.
896
937
final Color ? dividerColor;
897
938
939
+ /// The height of the divider.
940
+ ///
941
+ /// If null and [ThemeData.useMaterial3] is true, [TabBarTheme.dividerHeight] is used.
942
+ /// If that is also null and [ThemeData.useMaterial3] is true, 1.0 will be used.
943
+ /// Otherwise divider will not be drawn.
944
+ final double ? dividerHeight;
945
+
898
946
/// The color of selected tab labels.
899
947
///
900
948
/// If null, then [TabBarTheme.labelColor] is used. If that is also null and
@@ -1154,8 +1202,8 @@ class _TabBarState extends State<TabBar> {
1154
1202
TabBarTheme get _defaults {
1155
1203
if (Theme .of (context).useMaterial3) {
1156
1204
return widget._isPrimary
1157
- ? _TabsPrimaryDefaultsM3 (context, widget.isScrollable)
1158
- : _TabsSecondaryDefaultsM3 (context, widget.isScrollable);
1205
+ ? _TabsPrimaryDefaultsM3 (context, widget.isScrollable)
1206
+ : _TabsSecondaryDefaultsM3 (context, widget.isScrollable);
1159
1207
} else {
1160
1208
return _TabsDefaultsM2 (context, widget.isScrollable);
1161
1209
}
@@ -1269,8 +1317,10 @@ class _TabBarState extends State<TabBar> {
1269
1317
indicatorPadding: widget.indicatorPadding,
1270
1318
tabKeys: _tabKeys,
1271
1319
old: _indicatorPainter,
1272
- dividerColor: theme.useMaterial3 ? widget.dividerColor ?? tabBarTheme.dividerColor ?? _defaults.dividerColor : null ,
1273
1320
labelPaddings: _labelPaddings,
1321
+ dividerColor: widget.dividerColor ?? tabBarTheme.dividerColor ?? _defaults.dividerColor,
1322
+ dividerHeight: widget.dividerHeight ?? tabBarTheme.dividerHeight ?? _defaults.dividerHeight,
1323
+ showDivider: theme.useMaterial3 && ! widget.isScrollable,
1274
1324
);
1275
1325
}
1276
1326
@@ -1299,7 +1349,9 @@ class _TabBarState extends State<TabBar> {
1299
1349
widget.indicatorWeight != oldWidget.indicatorWeight ||
1300
1350
widget.indicatorSize != oldWidget.indicatorSize ||
1301
1351
widget.indicatorPadding != oldWidget.indicatorPadding ||
1302
- widget.indicator != oldWidget.indicator) {
1352
+ widget.indicator != oldWidget.indicator ||
1353
+ widget.dividerColor != oldWidget.dividerColor ||
1354
+ widget.dividerHeight != oldWidget.dividerHeight) {
1303
1355
_initIndicatorPainter ();
1304
1356
}
1305
1357
@@ -1475,6 +1527,7 @@ class _TabBarState extends State<TabBar> {
1475
1527
Widget build (BuildContext context) {
1476
1528
assert (debugCheckHasMaterialLocalizations (context));
1477
1529
assert (_debugScheduleCheckHasValidTabsCount ());
1530
+ final ThemeData theme = Theme .of (context);
1478
1531
final TabBarTheme tabBarTheme = TabBarTheme .of (context);
1479
1532
final TabAlignment effectiveTabAlignment = widget.tabAlignment ?? tabBarTheme.tabAlignment ?? _defaults.tabAlignment! ;
1480
1533
assert (_debugTabAlignmentIsValid (effectiveTabAlignment));
@@ -1486,7 +1539,6 @@ class _TabBarState extends State<TabBar> {
1486
1539
);
1487
1540
}
1488
1541
1489
-
1490
1542
final List <Widget > wrappedTabs = List <Widget >.generate (widget.tabs.length, (int index) {
1491
1543
const double verticalAdjustment = (_kTextAndIconTabHeight - _kTabHeight)/ 2.0 ;
1492
1544
EdgeInsetsGeometry ? adjustedPadding;
@@ -1627,6 +1679,24 @@ class _TabBarState extends State<TabBar> {
1627
1679
child: tabBar,
1628
1680
),
1629
1681
);
1682
+ if (theme.useMaterial3) {
1683
+ final AlignmentGeometry effectiveAlignment = switch (effectiveTabAlignment) {
1684
+ TabAlignment .center => Alignment .center,
1685
+ TabAlignment .start || TabAlignment .startOffset || TabAlignment .fill => AlignmentDirectional .centerStart,
1686
+ };
1687
+
1688
+ tabBar = CustomPaint (
1689
+ painter: _DividerPainter (
1690
+ dividerColor: widget.dividerColor ?? tabBarTheme.dividerColor ?? _defaults.dividerColor! ,
1691
+ dividerHeight: widget.dividerHeight ?? tabBarTheme.dividerHeight ?? _defaults.dividerHeight! ,
1692
+ ),
1693
+ child: Align (
1694
+ heightFactor: 1.0 ,
1695
+ alignment: effectiveAlignment,
1696
+ child: tabBar,
1697
+ ),
1698
+ );
1699
+ }
1630
1700
} else if (widget.padding != null ) {
1631
1701
tabBar = Padding (
1632
1702
padding: widget.padding! ,
@@ -2177,6 +2247,9 @@ class _TabsPrimaryDefaultsM3 extends TabBarTheme {
2177
2247
@override
2178
2248
Color ? get dividerColor => _colors.surfaceVariant;
2179
2249
2250
+ @override
2251
+ double ? get dividerHeight => 1.0 ;
2252
+
2180
2253
@override
2181
2254
Color ? get indicatorColor => _colors.primary;
2182
2255
@@ -2224,7 +2297,7 @@ class _TabsPrimaryDefaultsM3 extends TabBarTheme {
2224
2297
InteractiveInkFeatureFactory ? get splashFactory => Theme .of (context).splashFactory;
2225
2298
2226
2299
@override
2227
- TabAlignment ? get tabAlignment => isScrollable ? TabAlignment .start : TabAlignment .fill;
2300
+ TabAlignment ? get tabAlignment => isScrollable ? TabAlignment .startOffset : TabAlignment .fill;
2228
2301
2229
2302
static double indicatorWeight = 3.0 ;
2230
2303
}
@@ -2241,6 +2314,9 @@ class _TabsSecondaryDefaultsM3 extends TabBarTheme {
2241
2314
@override
2242
2315
Color ? get dividerColor => _colors.surfaceVariant;
2243
2316
2317
+ @override
2318
+ double ? get dividerHeight => 1.0 ;
2319
+
2244
2320
@override
2245
2321
Color ? get indicatorColor => _colors.primary;
2246
2322
@@ -2288,7 +2364,7 @@ class _TabsSecondaryDefaultsM3 extends TabBarTheme {
2288
2364
InteractiveInkFeatureFactory ? get splashFactory => Theme .of (context).splashFactory;
2289
2365
2290
2366
@override
2291
- TabAlignment ? get tabAlignment => isScrollable ? TabAlignment .start : TabAlignment .fill;
2367
+ TabAlignment ? get tabAlignment => isScrollable ? TabAlignment .startOffset : TabAlignment .fill;
2292
2368
}
2293
2369
2294
2370
// END GENERATED TOKEN PROPERTIES - Tabs
0 commit comments