blob: d8d781f02bd317f881b9bd5e282830e1763412a4 [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.
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import '../image_data.dart';
import '../widgets/semantics_tester.dart';
Future<void> pumpWidgetWithBoilerplate(WidgetTester tester, Widget widget) async {
await tester.pumpWidget(
Localizations(
locale: const Locale('en', 'US'),
delegates: const <LocalizationsDelegate<dynamic>>[
DefaultWidgetsLocalizations.delegate,
DefaultCupertinoLocalizations.delegate,
],
child: Directionality(
textDirection: TextDirection.ltr,
child: widget,
),
),
);
}
Future<void> main() async {
testWidgets('Need at least 2 tabs', (WidgetTester tester) async {
await expectLater(
() => pumpWidgetWithBoilerplate(tester, CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 1',
),
],
)),
throwsA(isAssertionError.having(
(AssertionError error) => error.toString(),
'.toString()',
contains('items.length'),
)),
);
});
testWidgets('Active and inactive colors', (WidgetTester tester) async {
await pumpWidgetWithBoilerplate(tester, MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 2',
),
],
currentIndex: 1,
activeColor: const Color(0xFF123456),
inactiveColor: const Color(0xFF654321),
),
));
final RichText actualInactive = tester.widget(find.descendant(
of: find.text('Tab 1'),
matching: find.byType(RichText),
));
expect(actualInactive.text.style!.color, const Color(0xFF654321));
final RichText actualActive = tester.widget(find.descendant(
of: find.text('Tab 2'),
matching: find.byType(RichText),
));
expect(actualActive.text.style!.color, const Color(0xFF123456));
});
testWidgets('BottomNavigationBar.label will create a text widget', (WidgetTester tester) async {
await pumpWidgetWithBoilerplate(tester, MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 2',
),
],
currentIndex: 1,
),
));
expect(find.text('Tab 1'), findsOneWidget);
expect(find.text('Tab 2'), findsOneWidget);
});
testWidgets('Active and inactive colors dark mode', (WidgetTester tester) async {
const CupertinoDynamicColor dynamicActiveColor = CupertinoDynamicColor.withBrightness(
color: Color(0xFF000000),
darkColor: Color(0xFF000001),
);
const CupertinoDynamicColor dynamicInactiveColor = CupertinoDynamicColor.withBrightness(
color: Color(0xFF000002),
darkColor: Color(0xFF000003),
);
await pumpWidgetWithBoilerplate(tester, MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 2',
),
],
currentIndex: 1,
activeColor: dynamicActiveColor,
inactiveColor: dynamicInactiveColor,
),
));
RichText actualInactive = tester.widget(find.descendant(
of: find.text('Tab 1'),
matching: find.byType(RichText),
));
expect(actualInactive.text.style!.color!.value, 0xFF000002);
RichText actualActive = tester.widget(find.descendant(
of: find.text('Tab 2'),
matching: find.byType(RichText),
));
expect(actualActive.text.style!.color!.value, 0xFF000000);
final RenderDecoratedBox renderDecoratedBox = tester.renderObject(find.descendant(
of: find.byType(BackdropFilter),
matching: find.byType(DecoratedBox),
));
// Border color is resolved correctly.
final BoxDecoration decoration1 = renderDecoratedBox.decoration as BoxDecoration;
expect(decoration1.border!.top.color.value, 0x4C000000);
// Switch to dark mode.
await pumpWidgetWithBoilerplate(tester, MediaQuery(
data: const MediaQueryData(platformBrightness: Brightness.dark),
child: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 2',
),
],
currentIndex: 1,
activeColor: dynamicActiveColor,
inactiveColor: dynamicInactiveColor,
),
));
actualInactive = tester.widget(find.descendant(
of: find.text('Tab 1'),
matching: find.byType(RichText),
));
expect(actualInactive.text.style!.color!.value, 0xFF000003);
actualActive = tester.widget(find.descendant(
of: find.text('Tab 2'),
matching: find.byType(RichText),
));
expect(actualActive.text.style!.color!.value, 0xFF000001);
// Border color is resolved correctly.
final BoxDecoration decoration2 = renderDecoratedBox.decoration as BoxDecoration;
expect(decoration2.border!.top.color.value, 0x29000000);
});
testWidgets('Tabs respects themes', (WidgetTester tester) async {
await tester.pumpWidget(
CupertinoApp(
home: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 2',
),
],
currentIndex: 1,
),
),
);
RichText actualInactive = tester.widget(find.descendant(
of: find.text('Tab 1'),
matching: find.byType(RichText),
));
expect(actualInactive.text.style!.color!.value, 0xFF999999);
RichText actualActive = tester.widget(find.descendant(
of: find.text('Tab 2'),
matching: find.byType(RichText),
));
expect(actualActive.text.style!.color, CupertinoColors.activeBlue);
await tester.pumpWidget(
CupertinoApp(
theme: const CupertinoThemeData(brightness: Brightness.dark),
home: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 2',
),
],
currentIndex: 1,
),
),
);
actualInactive = tester.widget(find.descendant(
of: find.text('Tab 1'),
matching: find.byType(RichText),
));
expect(actualInactive.text.style!.color!.value, 0xFF757575);
actualActive = tester.widget(find.descendant(
of: find.text('Tab 2'),
matching: find.byType(RichText),
));
expect(actualActive.text.style!.color, isSameColorAs(CupertinoColors.activeBlue.darkColor));
});
testWidgets('Use active icon', (WidgetTester tester) async {
final MemoryImage activeIcon = MemoryImage(Uint8List.fromList(kBlueSquarePng));
final MemoryImage inactiveIcon = MemoryImage(Uint8List.fromList(kTransparentImage));
await pumpWidgetWithBoilerplate(tester, MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(inactiveIcon),
activeIcon: ImageIcon(activeIcon),
label: 'Tab 2',
),
],
currentIndex: 1,
activeColor: const Color(0xFF123456),
inactiveColor: const Color(0xFF654321),
),
));
final Image image = tester.widget(find.descendant(
of: find.widgetWithText(GestureDetector, 'Tab 2'),
matching: find.byType(Image),
));
expect(image.color, const Color(0xFF123456));
expect(image.image, activeIcon);
});
testWidgets('Adjusts height to account for bottom padding', (WidgetTester tester) async {
final CupertinoTabBar tabBar = CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Aka',
),
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Shiro',
),
],
);
// Verify height with no bottom padding.
await pumpWidgetWithBoilerplate(tester, MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabScaffold(
tabBar: tabBar,
tabBuilder: (BuildContext context, int index) {
return const Placeholder();
},
),
));
expect(tester.getSize(find.byType(CupertinoTabBar)).height, 50.0);
// Verify height with bottom padding.
await pumpWidgetWithBoilerplate(tester, MediaQuery(
data: const MediaQueryData(padding: EdgeInsets.only(bottom: 40.0)),
child: CupertinoTabScaffold(
tabBar: tabBar,
tabBuilder: (BuildContext context, int index) {
return const Placeholder();
},
),
));
expect(tester.getSize(find.byType(CupertinoTabBar)).height, 90.0);
});
testWidgets('Opaque background does not add blur effects', (WidgetTester tester) async {
await pumpWidgetWithBoilerplate(tester, MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 2',
),
],
),
));
expect(find.byType(BackdropFilter), findsOneWidget);
await pumpWidgetWithBoilerplate(tester, MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 2',
),
],
backgroundColor: const Color(0xFFFFFFFF), // Opaque white.
),
));
expect(find.byType(BackdropFilter), findsNothing);
});
testWidgets('Tap callback', (WidgetTester tester) async {
late int callbackTab;
await pumpWidgetWithBoilerplate(tester, MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 2',
),
],
currentIndex: 1,
onTap: (int tab) { callbackTab = tab; },
),
));
await tester.tap(find.text('Tab 1'));
expect(callbackTab, 0);
await tester.tap(find.text('Tab 2'));
expect(callbackTab, 1);
});
testWidgets('tabs announce semantics', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await pumpWidgetWithBoilerplate(tester, MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(MemoryImage(Uint8List.fromList(kTransparentImage))),
label: 'Tab 2',
),
],
),
));
expect(semantics, includesNodeWith(
label: 'Tab 1',
hint: 'Tab 1 of 2',
flags: <SemanticsFlag>[SemanticsFlag.isSelected],
textDirection: TextDirection.ltr,
));
expect(semantics, includesNodeWith(
label: 'Tab 2',
hint: 'Tab 2 of 2',
textDirection: TextDirection.ltr,
));
semantics.dispose();
});
testWidgets('Label of items should be nullable', (WidgetTester tester) async {
final MemoryImage iconProvider = MemoryImage(Uint8List.fromList(kTransparentImage));
final List<int> itemsTapped = <int>[];
await pumpWidgetWithBoilerplate(
tester,
MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(
MemoryImage(Uint8List.fromList(kTransparentImage)),
),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(
iconProvider,
),
),
],
onTap: (int index) => itemsTapped.add(index),
),
),
);
expect(find.text('Tab 1'), findsOneWidget);
final Finder finder = find.byWidgetPredicate(
(Widget widget) => widget is Image && widget.image == iconProvider,
);
await tester.tap(finder);
expect(itemsTapped, <int>[1]);
});
testWidgets('Hide border hides the top border of the tabBar', (WidgetTester tester) async {
await pumpWidgetWithBoilerplate(
tester,
MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(
MemoryImage(Uint8List.fromList(kTransparentImage)),
),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(
MemoryImage(Uint8List.fromList(kTransparentImage)),
),
label: 'Tab 2',
),
],
),
),
);
final DecoratedBox decoratedBox = tester.widget(find.byType(DecoratedBox));
final BoxDecoration boxDecoration = decoratedBox.decoration as BoxDecoration;
expect(boxDecoration.border, isNotNull);
await pumpWidgetWithBoilerplate(
tester,
MediaQuery(
data: const MediaQueryData(),
child: CupertinoTabBar(
items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: ImageIcon(
MemoryImage(Uint8List.fromList(kTransparentImage)),
),
label: 'Tab 1',
),
BottomNavigationBarItem(
icon: ImageIcon(
MemoryImage(Uint8List.fromList(kTransparentImage)),
),
label: 'Tab 2',
),
],
backgroundColor: const Color(0xFFFFFFFF), // Opaque white.
border: null,
),
),
);
final DecoratedBox decoratedBoxHiddenBorder =
tester.widget(find.byType(DecoratedBox));
final BoxDecoration boxDecorationHiddenBorder =
decoratedBoxHiddenBorder.decoration as BoxDecoration;
expect(boxDecorationHiddenBorder.border, isNull);
});
}