Unverified Commit 2ffc5bc1 authored by zmtzawqlp's avatar zmtzawqlp Committed by GitHub

Fix wrong position of TabBarIndicator when it's label size and has label padding (#116398)

parent e6ec0871
......@@ -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),
......@@ -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
......@@ -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);
......@@ -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
......@@ -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() {
......@@ -1063,6 +1069,7 @@ class _TabBarState extends State<TabBar> {
tabKeys: _tabKeys,
old: _indicatorPainter,
dividerColor: theme.useMaterial3 ? widget.dividerColor ?? defaults.dividerColor : null,
labelPaddings: _labelPaddings,
);
}
......@@ -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);
}
}
......@@ -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],
......
......@@ -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:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment