blob: 18a63c4c7e2c5f3ee4548d5bae104f1262a3679a [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file is run as part of a reduced test set in CI on Mac and Windows
// machines.
@Tags(<String>['reduced-test-set'])
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart';
const String _tab1Text = 'tab 1';
const String _tab2Text = 'tab 2';
const String _tab3Text = 'tab 3';
final Key _painterKey = UniqueKey();
const List<Tab> _tabs = <Tab>[
Tab(text: _tab1Text, icon: Icon(Icons.looks_one)),
Tab(text: _tab2Text, icon: Icon(Icons.looks_two)),
Tab(text: _tab3Text, icon: Icon(Icons.looks_3)),
];
final List<SizedBox> _sizedTabs = <SizedBox>[
SizedBox(key: UniqueKey(), width: 100.0, height: 50.0),
SizedBox(key: UniqueKey(), width: 100.0, height: 50.0),
];
Widget _withTheme(
TabBarTheme? theme, {
List<Widget> tabs = _tabs,
bool isScrollable = false,
bool useMaterial3 = false,
}) {
return MaterialApp(
theme: ThemeData(tabBarTheme: theme, useMaterial3: useMaterial3),
home: Scaffold(
body: RepaintBoundary(
key: _painterKey,
child: TabBar(
tabs: tabs,
isScrollable: isScrollable,
controller: TabController(length: tabs.length, vsync: const TestVSync()),
),
),
),
);
}
RenderParagraph _iconRenderObject(WidgetTester tester, IconData icon) {
return tester.renderObject<RenderParagraph>(
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
);
}
void main() {
test('TabBarTheme copyWith, ==, hashCode, defaults', () {
expect(const TabBarTheme(), const TabBarTheme().copyWith());
expect(const TabBarTheme().hashCode, const TabBarTheme().copyWith().hashCode);
expect(const TabBarTheme().indicator, null);
expect(const TabBarTheme().indicatorColor, null);
expect(const TabBarTheme().indicatorSize, null);
expect(const TabBarTheme().dividerColor, null);
expect(const TabBarTheme().labelColor, null);
expect(const TabBarTheme().labelPadding, null);
expect(const TabBarTheme().labelStyle, null);
expect(const TabBarTheme().unselectedLabelColor, null);
expect(const TabBarTheme().unselectedLabelStyle, null);
expect(const TabBarTheme().overlayColor, null);
expect(const TabBarTheme().splashFactory, null);
expect(const TabBarTheme().mouseCursor, null);
});
testWidgets('Tab bar defaults', (WidgetTester tester) async {
// tests for the default label color and label styles when tabBarTheme and tabBar do not provide any
await tester.pumpWidget(_withTheme(null, useMaterial3: true));
final ThemeData theme = ThemeData(useMaterial3: true);
final RenderParagraph selectedRenderObject = tester.renderObject<RenderParagraph>(find.text(_tab1Text));
expect(selectedRenderObject.text.style!.fontFamily, equals(theme.textTheme.titleSmall!.fontFamily));
expect(selectedRenderObject.text.style!.fontSize, equals(14.0));
expect(selectedRenderObject.text.style!.color, equals(theme.colorScheme.primary));
final RenderParagraph unselectedRenderObject = tester.renderObject<RenderParagraph>(find.text(_tab2Text));
expect(unselectedRenderObject.text.style!.fontFamily, equals(theme.textTheme.titleSmall!.fontFamily));
expect(unselectedRenderObject.text.style!.fontSize, equals(14.0));
expect(unselectedRenderObject.text.style!.color, equals(theme.colorScheme.onSurfaceVariant));
// tests for the default value of labelPadding when tabBarTheme and tabBar do not provide one
await tester.pumpWidget(_withTheme(null, tabs: _sizedTabs, isScrollable: true));
const double indicatorWeight = 2.0;
final Rect tabBar = tester.getRect(find.byType(TabBar));
final Rect tabOneRect = tester.getRect(find.byKey(_sizedTabs[0].key!));
final Rect tabTwoRect = tester.getRect(find.byKey(_sizedTabs[1].key!));
// verify coordinates of tabOne
expect(tabOneRect.left, equals(kTabLabelPadding.left));
expect(tabOneRect.top, equals(kTabLabelPadding.top));
expect(tabOneRect.bottom, equals(tabBar.bottom - kTabLabelPadding.bottom - indicatorWeight));
// verify coordinates of tabTwo
expect(tabTwoRect.right, equals(tabBar.width - kTabLabelPadding.right));
expect(tabTwoRect.top, equals(kTabLabelPadding.top));
expect(tabTwoRect.bottom, equals(tabBar.bottom - kTabLabelPadding.bottom - indicatorWeight));
// verify tabOne and tabTwo is separated by right padding of tabOne and left padding of tabTwo
expect(tabOneRect.right, equals(tabTwoRect.left - kTabLabelPadding.left - kTabLabelPadding.right));
final RenderBox tabBarBox = tester.firstRenderObject<RenderBox>(find.byType(TabBar));
expect(
tabBarBox,
paints
..line(color: theme.colorScheme.surfaceVariant)
..rrect(color: theme.colorScheme.primary),
);
});
testWidgets('Tab bar theme overrides label color (selected)', (WidgetTester tester) async {
const Color labelColor = Colors.black;
const TabBarTheme tabBarTheme = TabBarTheme(labelColor: labelColor);
await tester.pumpWidget(_withTheme(tabBarTheme));
final RenderParagraph textRenderObject = tester.renderObject<RenderParagraph>(find.text(_tab1Text));
expect(textRenderObject.text.style!.color, equals(labelColor));
final RenderParagraph iconRenderObject = _iconRenderObject(tester, Icons.looks_one);
expect(iconRenderObject.text.style!.color, equals(labelColor));
});
testWidgets('Tab bar theme overrides label padding', (WidgetTester tester) async {
const double topPadding = 10.0;
const double bottomPadding = 7.0;
const double rightPadding = 13.0;
const double leftPadding = 16.0;
const double indicatorWeight = 2.0; // default value
const EdgeInsetsGeometry labelPadding = EdgeInsets.fromLTRB(
leftPadding, topPadding, rightPadding, bottomPadding,
);
const TabBarTheme tabBarTheme = TabBarTheme(labelPadding: labelPadding);
await tester.pumpWidget(_withTheme(
tabBarTheme,
tabs: _sizedTabs,
isScrollable: true,
));
final Rect tabBar = tester.getRect(find.byType(TabBar));
final Rect tabOneRect = tester.getRect(find.byKey(_sizedTabs[0].key!));
final Rect tabTwoRect = tester.getRect(find.byKey(_sizedTabs[1].key!));
// verify coordinates of tabOne
expect(tabOneRect.left, equals(leftPadding));
expect(tabOneRect.top, equals(topPadding));
expect(tabOneRect.bottom, equals(tabBar.bottom - bottomPadding - indicatorWeight));
// verify coordinates of tabTwo
expect(tabTwoRect.right, equals(tabBar.width - rightPadding));
expect(tabTwoRect.top, equals(topPadding));
expect(tabTwoRect.bottom, equals(tabBar.bottom - bottomPadding - indicatorWeight));
// verify tabOne and tabTwo are separated by right padding of tabOne and left padding of tabTwo
expect(tabOneRect.right, equals(tabTwoRect.left - leftPadding - rightPadding));
});
testWidgets('Tab bar theme overrides label styles', (WidgetTester tester) async {
const TextStyle labelStyle = TextStyle(fontFamily: 'foobar');
const TextStyle unselectedLabelStyle = TextStyle(fontFamily: 'baz');
const TabBarTheme tabBarTheme = TabBarTheme(
labelStyle: labelStyle,
unselectedLabelStyle: unselectedLabelStyle,
);
await tester.pumpWidget(_withTheme(tabBarTheme));
final RenderParagraph selectedRenderObject = tester.renderObject<RenderParagraph>(find.text(_tab1Text));
expect(selectedRenderObject.text.style!.fontFamily, equals(labelStyle.fontFamily));
final RenderParagraph unselectedRenderObject = tester.renderObject<RenderParagraph>(find.text(_tab2Text));
expect(unselectedRenderObject.text.style!.fontFamily, equals(unselectedLabelStyle.fontFamily));
});
testWidgets('Tab bar theme with just label style specified', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/28784
const TextStyle labelStyle = TextStyle(fontFamily: 'foobar');
const TabBarTheme tabBarTheme = TabBarTheme(
labelStyle: labelStyle,
);
await tester.pumpWidget(_withTheme(tabBarTheme));
final RenderParagraph selectedRenderObject = tester.renderObject<RenderParagraph>(find.text(_tab1Text));
expect(selectedRenderObject.text.style!.fontFamily, equals(labelStyle.fontFamily));
final RenderParagraph unselectedRenderObject = tester.renderObject<RenderParagraph>(find.text(_tab2Text));
expect(unselectedRenderObject.text.style!.fontFamily, equals('Roboto'));
expect(unselectedRenderObject.text.style!.fontSize, equals(14.0));
expect(unselectedRenderObject.text.style!.color, equals(Colors.white.withAlpha(0xB2)));
});
testWidgets('Tab bar label styles override theme label styles', (WidgetTester tester) async {
const TextStyle labelStyle = TextStyle(fontFamily: '1');
const TextStyle unselectedLabelStyle = TextStyle(fontFamily: '2');
const TextStyle themeLabelStyle = TextStyle(fontFamily: '3');
const TextStyle themeUnselectedLabelStyle = TextStyle(fontFamily: '4');
const TabBarTheme tabBarTheme = TabBarTheme(
labelStyle: themeLabelStyle,
unselectedLabelStyle: themeUnselectedLabelStyle,
);
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(tabBarTheme: tabBarTheme),
home: Scaffold(body: TabBar(
tabs: _tabs,
controller: TabController(length: _tabs.length, vsync: const TestVSync()),
labelStyle: labelStyle,
unselectedLabelStyle: unselectedLabelStyle,
),
),
),
);
final RenderParagraph selectedRenderObject = tester.renderObject<RenderParagraph>(find.text(_tab1Text));
expect(selectedRenderObject.text.style!.fontFamily, equals(labelStyle.fontFamily));
final RenderParagraph unselectedRenderObject = tester.renderObject<RenderParagraph>(find.text(_tab2Text));
expect(unselectedRenderObject.text.style!.fontFamily, equals(unselectedLabelStyle.fontFamily));
});
testWidgets('Tab bar label padding overrides theme label padding', (WidgetTester tester) async {
const double verticalPadding = 10.0;
const double horizontalPadding = 10.0;
const EdgeInsetsGeometry labelPadding = EdgeInsets.symmetric(
vertical: verticalPadding,
horizontal: horizontalPadding,
);
const double verticalThemePadding = 20.0;
const double horizontalThemePadding = 20.0;
const EdgeInsetsGeometry themeLabelPadding = EdgeInsets.symmetric(
vertical: verticalThemePadding,
horizontal: horizontalThemePadding,
);
const double indicatorWeight = 2.0; // default value
const TabBarTheme tabBarTheme = TabBarTheme(labelPadding: themeLabelPadding);
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(tabBarTheme: tabBarTheme),
home: Scaffold(body:
RepaintBoundary(
key: _painterKey,
child: TabBar(
tabs: _sizedTabs,
isScrollable: true,
controller: TabController(length: _sizedTabs.length, vsync: const TestVSync()),
labelPadding: labelPadding,
),
),
),
),
);
final Rect tabBar = tester.getRect(find.byType(TabBar));
final Rect tabOneRect = tester.getRect(find.byKey(_sizedTabs[0].key!));
final Rect tabTwoRect = tester.getRect(find.byKey(_sizedTabs[1].key!));
// verify coordinates of tabOne
expect(tabOneRect.left, equals(horizontalPadding));
expect(tabOneRect.top, equals(verticalPadding));
expect(tabOneRect.bottom, equals(tabBar.bottom - verticalPadding - indicatorWeight));
// verify coordinates of tabTwo
expect(tabTwoRect.right, equals(tabBar.width - horizontalPadding));
expect(tabTwoRect.top, equals(verticalPadding));
expect(tabTwoRect.bottom, equals(tabBar.bottom - verticalPadding - indicatorWeight));
// verify tabOne and tabTwo are separated by 2x horizontalPadding
expect(tabOneRect.right, equals(tabTwoRect.left - (2 * horizontalPadding)));
});
testWidgets('Tab bar theme overrides label color (unselected)', (WidgetTester tester) async {
const Color unselectedLabelColor = Colors.black;
const TabBarTheme tabBarTheme = TabBarTheme(unselectedLabelColor: unselectedLabelColor);
await tester.pumpWidget(_withTheme(tabBarTheme));
final RenderParagraph textRenderObject = tester.renderObject<RenderParagraph>(find.text(_tab2Text));
expect(textRenderObject.text.style!.color, equals(unselectedLabelColor));
final RenderParagraph iconRenderObject = _iconRenderObject(tester, Icons.looks_two);
expect(iconRenderObject.text.style!.color, equals(unselectedLabelColor));
});
testWidgets('Tab bar default tab indicator size', (WidgetTester tester) async {
await tester.pumpWidget(_withTheme(null, useMaterial3: true, isScrollable: true));
await expectLater(
find.byKey(_painterKey),
matchesGoldenFile('tab_bar.default.tab_indicator_size.png'),
);
});
testWidgets('Tab bar theme overrides tab indicator size (tab)', (WidgetTester tester) async {
const TabBarTheme tabBarTheme = TabBarTheme(indicatorSize: TabBarIndicatorSize.tab);
await tester.pumpWidget(_withTheme(tabBarTheme));
await expectLater(
find.byKey(_painterKey),
matchesGoldenFile('tab_bar_theme.tab_indicator_size_tab.png'),
);
});
testWidgets('Tab bar theme overrides tab indicator size (label)', (WidgetTester tester) async {
const TabBarTheme tabBarTheme = TabBarTheme(indicatorSize: TabBarIndicatorSize.label);
await tester.pumpWidget(_withTheme(tabBarTheme));
await expectLater(
find.byKey(_painterKey),
matchesGoldenFile('tab_bar_theme.tab_indicator_size_label.png'),
);
});
testWidgets('Tab bar theme overrides tab mouse cursor', (WidgetTester tester) async {
const TabBarTheme tabBarTheme = TabBarTheme(mouseCursor: MaterialStateMouseCursor.textable);
await tester.pumpWidget(_withTheme(tabBarTheme));
final Offset tabBar = tester.getCenter(
find.ancestor(of: find.text('tab 1'),matching: find.byType(TabBar)),
);
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(tabBar);
await tester.pumpAndSettle();
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
});
testWidgets('Tab bar theme - custom tab indicator', (WidgetTester tester) async {
final TabBarTheme tabBarTheme = TabBarTheme(
indicator: BoxDecoration(
border: Border.all(),
),
);
await tester.pumpWidget(_withTheme(tabBarTheme));
await expectLater(
find.byKey(_painterKey),
matchesGoldenFile('tab_bar_theme.custom_tab_indicator.png'),
);
});
testWidgets('Tab bar theme - beveled rect indicator', (WidgetTester tester) async {
const TabBarTheme tabBarTheme = TabBarTheme(
indicator: ShapeDecoration(
shape: BeveledRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(20.0))),
color: Colors.black,
),
);
await tester.pumpWidget(_withTheme(tabBarTheme));
await expectLater(
find.byKey(_painterKey),
matchesGoldenFile('tab_bar_theme.beveled_rect_indicator.png'),
);
});
group('Material 2', () {
// Tests that are only relevant for Material 2. Once ThemeData.useMaterial3
// is turned on by default, these tests can be removed.
testWidgets('Tab bar defaults', (WidgetTester tester) async {
// tests for the default label color and label styles when tabBarTheme and tabBar do not provide any
await tester.pumpWidget(_withTheme(null));
final RenderParagraph selectedRenderObject = tester.renderObject<RenderParagraph>(find.text(_tab1Text));
expect(selectedRenderObject.text.style!.fontFamily, equals('Roboto'));
expect(selectedRenderObject.text.style!.fontSize, equals(14.0));
expect(selectedRenderObject.text.style!.color, equals(Colors.white));
final RenderParagraph unselectedRenderObject = tester.renderObject<RenderParagraph>(find.text(_tab2Text));
expect(unselectedRenderObject.text.style!.fontFamily, equals('Roboto'));
expect(unselectedRenderObject.text.style!.fontSize, equals(14.0));
expect(unselectedRenderObject.text.style!.color, equals(Colors.white.withAlpha(0xB2)));
// tests for the default value of labelPadding when tabBarTheme and tabBar do not provide one
await tester.pumpWidget(_withTheme(null, tabs: _sizedTabs, isScrollable: true));
const double indicatorWeight = 2.0;
final Rect tabBar = tester.getRect(find.byType(TabBar));
final Rect tabOneRect = tester.getRect(find.byKey(_sizedTabs[0].key!));
final Rect tabTwoRect = tester.getRect(find.byKey(_sizedTabs[1].key!));
// verify coordinates of tabOne
expect(tabOneRect.left, equals(kTabLabelPadding.left));
expect(tabOneRect.top, equals(kTabLabelPadding.top));
expect(tabOneRect.bottom, equals(tabBar.bottom - kTabLabelPadding.bottom - indicatorWeight));
// verify coordinates of tabTwo
expect(tabTwoRect.right, equals(tabBar.width - kTabLabelPadding.right));
expect(tabTwoRect.top, equals(kTabLabelPadding.top));
expect(tabTwoRect.bottom, equals(tabBar.bottom - kTabLabelPadding.bottom - indicatorWeight));
// verify tabOne and tabTwo is separated by right padding of tabOne and left padding of tabTwo
expect(tabOneRect.right, equals(tabTwoRect.left - kTabLabelPadding.left - kTabLabelPadding.right));
final RenderBox tabBarBox = tester.firstRenderObject<RenderBox>(find.byType(TabBar));
expect(tabBarBox, paints..line(color: const Color(0xff2196f3)));
});
testWidgets('Tab bar default tab indicator size', (WidgetTester tester) async {
await tester.pumpWidget(_withTheme(null));
await expectLater(
find.byKey(_painterKey),
matchesGoldenFile('tab_bar.m2.default.tab_indicator_size.png'),
);
});
});
}