Skip to content

Commit

Permalink
Fix wrong position of TabBarIndicator when it's label size and has la…
Browse files Browse the repository at this point in the history
…bel padding (#116398)
  • Loading branch information
zmtzawqlp authored Dec 8, 2022
1 parent e6ec087 commit 2ffc5bc
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 4 deletions.
19 changes: 15 additions & 4 deletions packages/flutter/lib/src/material/tabs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ class _IndicatorPainter extends CustomPainter {
required this.tabKeys,
required _IndicatorPainter? old,
required this.indicatorPadding,
required this.labelPaddings,
this.dividerColor,
}) : assert(controller != null),
assert(indicator != null),
Expand All @@ -347,6 +348,7 @@ class _IndicatorPainter extends CustomPainter {
final EdgeInsetsGeometry indicatorPadding;
final List<GlobalKey> tabKeys;
final Color? dividerColor;
final List<EdgeInsetsGeometry> labelPaddings;

// _currentTabOffsets and _currentTextDirection are set each time TabBar
// layout is completed. These values can be null when TabBar contains no
Expand Down Expand Up @@ -402,9 +404,11 @@ class _IndicatorPainter extends CustomPainter {

if (indicatorSize == TabBarIndicatorSize.label) {
final double tabWidth = tabKeys[tabIndex].currentContext!.size!.width;
final double delta = ((tabRight - tabLeft) - tabWidth) / 2.0;
tabLeft += delta;
tabRight -= delta;
final EdgeInsetsGeometry labelPadding = labelPaddings[tabIndex];
final EdgeInsets insets = labelPadding.resolve(_currentTextDirection);
final double delta = ((tabRight - tabLeft) - (tabWidth + insets.horizontal)) / 2.0;
tabLeft += delta + insets.left;
tabRight = tabLeft + tabWidth;
}

final EdgeInsets insets = indicatorPadding.resolve(_currentTextDirection);
Expand Down Expand Up @@ -952,6 +956,7 @@ class _TabBarState extends State<TabBar> {
int? _currentIndex;
late double _tabStripWidth;
late List<GlobalKey> _tabKeys;
late List<EdgeInsetsGeometry> _labelPaddings;
bool _debugHasScheduledValidTabsCountCheck = false;

@override
Expand All @@ -960,6 +965,7 @@ class _TabBarState extends State<TabBar> {
// If indicatorSize is TabIndicatorSize.label, _tabKeys[i] is used to find
// the width of tab widget i. See _IndicatorPainter.indicatorRect().
_tabKeys = widget.tabs.map((Widget tab) => GlobalKey()).toList();
_labelPaddings = List<EdgeInsetsGeometry>.filled(widget.tabs.length, EdgeInsets.zero, growable: true);
}

Decoration _getIndicator() {
Expand Down Expand Up @@ -1063,6 +1069,7 @@ class _TabBarState extends State<TabBar> {
tabKeys: _tabKeys,
old: _indicatorPainter,
dividerColor: theme.useMaterial3 ? widget.dividerColor ?? defaults.dividerColor : null,
labelPaddings: _labelPaddings,
);
}

Expand Down Expand Up @@ -1098,8 +1105,10 @@ class _TabBarState extends State<TabBar> {
if (widget.tabs.length > _tabKeys.length) {
final int delta = widget.tabs.length - _tabKeys.length;
_tabKeys.addAll(List<GlobalKey>.generate(delta, (int n) => GlobalKey()));
_labelPaddings.addAll(List<EdgeInsetsGeometry>.filled(delta, EdgeInsets.zero));
} else if (widget.tabs.length < _tabKeys.length) {
_tabKeys.removeRange(widget.tabs.length, _tabKeys.length);
_labelPaddings.removeRange(widget.tabs.length, _tabKeys.length);
}
}

Expand Down Expand Up @@ -1274,10 +1283,12 @@ class _TabBarState extends State<TabBar> {
}
}

_labelPaddings[index] = adjustedPadding ?? widget.labelPadding ?? tabBarTheme.labelPadding ?? kTabLabelPadding;

return Center(
heightFactor: 1.0,
child: Padding(
padding: adjustedPadding ?? widget.labelPadding ?? tabBarTheme.labelPadding ?? kTabLabelPadding,
padding: _labelPaddings[index],
child: KeyedSubtree(
key: _tabKeys[index],
child: widget.tabs[index],
Expand Down
71 changes: 71 additions & 0 deletions packages/flutter/test/material/tabs_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2788,6 +2788,77 @@ void main() {
));
});

testWidgets('TabBar with labelPadding(TabBarIndicatorSize.label)', (WidgetTester tester) async {
const double indicatorWeight = 2.0; // default indicator weight
const EdgeInsets labelPadding = EdgeInsets.only(left: 7.0, right: 4.0);
const EdgeInsets indicatorPadding = EdgeInsets.only(left: 3.0, right: 7.0);

final List<Widget> tabs = <Widget>[
SizedBox(key: UniqueKey(), width: 130.0, height: 30.0),
SizedBox(key: UniqueKey(), width: 140.0, height: 40.0),
SizedBox(key: UniqueKey(), width: 150.0, height: 50.0),
];

final TabController controller = TabController(
vsync: const TestVSync(),
length: tabs.length,
);

await tester.pumpWidget(
boilerplate(
child: Container(
alignment: Alignment.topLeft,
child: TabBar(
labelPadding: labelPadding,
indicatorPadding: indicatorPadding,
isScrollable: true,
controller: controller,
indicatorSize: TabBarIndicatorSize.label,
tabs: tabs,
),
),
),
);

final RenderBox tabBarBox = tester.firstRenderObject<RenderBox>(find.byType(TabBar));
const double tabBarHeight = 50.0 + indicatorWeight; // 50 = max tab height
expect(tabBarBox.size.height, tabBarHeight);

// Tab0 width = 130, height = 30
double tabLeft = labelPadding.left;
double tabRight = tabLeft + 130.0;
double tabTop = (tabBarHeight - indicatorWeight - 30.0) / 2.0;
double tabBottom = tabTop + 30.0;
Rect tabRect = Rect.fromLTRB(tabLeft, tabTop, tabRight, tabBottom);
expect(tester.getRect(find.byKey(tabs[0].key!)), tabRect);

// Tab1 width = 140, height = 40
tabLeft = tabRight + labelPadding.right + labelPadding.left;
tabRight = tabLeft + 140.0;
tabTop = (tabBarHeight - indicatorWeight - 40.0) / 2.0;
tabBottom = tabTop + 40.0;
tabRect = Rect.fromLTRB(tabLeft, tabTop, tabRight, tabBottom);
expect(tester.getRect(find.byKey(tabs[1].key!)), tabRect);

// Tab2 width = 150, height = 50
tabLeft = tabRight + labelPadding.right + labelPadding.left;
tabRight = tabLeft + 150.0;
tabTop = (tabBarHeight - indicatorWeight - 50.0) / 2.0;
tabBottom = tabTop + 50.0;
tabRect = Rect.fromLTRB(tabLeft, tabTop, tabRight, tabBottom);
expect(tester.getRect(find.byKey(tabs[2].key!)), tabRect);

// Tab 0 selected
final double indicatorLeft = indicatorPadding.left + labelPadding.left + indicatorWeight / 2.0;
final double indicatorRight = labelPadding.left + 130.0 - indicatorPadding.right - indicatorWeight / 2.0;
final double indicatorY = tabBottom + indicatorWeight / 2.0;
expect(tabBarBox, paints..line(
strokeWidth: indicatorWeight,
p1: Offset(indicatorLeft, indicatorY),
p2: Offset(indicatorRight, indicatorY),
));
});

testWidgets('Overflowing RTL tab bar', (WidgetTester tester) async {
final List<Widget> tabs = List<Widget>.filled(100,
// For convenience padded width of each tab will equal 100:
Expand Down

0 comments on commit 2ffc5bc

Please sign in to comment.