blob: f68c66babefe3af006afd3c5d1f48a7cf8265497 [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 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:vector_math/vector_math_64.dart' show Vector3;
void main() {
test('BottomNavigationBarThemeData copyWith, ==, hashCode basics', () {
expect(const BottomNavigationBarThemeData(), const BottomNavigationBarThemeData().copyWith());
expect(const BottomNavigationBarThemeData().hashCode, const BottomNavigationBarThemeData().copyWith().hashCode);
});
test('BottomNavigationBarThemeData lerp special cases', () {
const BottomNavigationBarThemeData data = BottomNavigationBarThemeData();
expect(identical(BottomNavigationBarThemeData.lerp(data, data, 0.5), data), true);
});
test('BottomNavigationBarThemeData defaults', () {
const BottomNavigationBarThemeData themeData = BottomNavigationBarThemeData();
expect(themeData.backgroundColor, null);
expect(themeData.elevation, null);
expect(themeData.selectedIconTheme, null);
expect(themeData.unselectedIconTheme, null);
expect(themeData.selectedItemColor, null);
expect(themeData.unselectedItemColor, null);
expect(themeData.selectedLabelStyle, null);
expect(themeData.unselectedLabelStyle, null);
expect(themeData.showSelectedLabels, null);
expect(themeData.showUnselectedLabels, null);
expect(themeData.type, null);
expect(themeData.landscapeLayout, null);
expect(themeData.mouseCursor, null);
const BottomNavigationBarTheme theme = BottomNavigationBarTheme(data: BottomNavigationBarThemeData(), child: SizedBox());
expect(theme.data.backgroundColor, null);
expect(theme.data.elevation, null);
expect(theme.data.selectedIconTheme, null);
expect(theme.data.unselectedIconTheme, null);
expect(theme.data.selectedItemColor, null);
expect(theme.data.unselectedItemColor, null);
expect(theme.data.selectedLabelStyle, null);
expect(theme.data.unselectedLabelStyle, null);
expect(theme.data.showSelectedLabels, null);
expect(theme.data.showUnselectedLabels, null);
expect(theme.data.type, null);
expect(themeData.landscapeLayout, null);
expect(themeData.mouseCursor, null);
});
testWidgets('Default BottomNavigationBarThemeData debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const BottomNavigationBarThemeData().debugFillProperties(builder);
final List<String> description = builder.properties
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description, <String>[]);
});
testWidgets('BottomNavigationBarThemeData implements debugFillProperties', (WidgetTester tester) async {
final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
const BottomNavigationBarThemeData(
backgroundColor: Color(0xfffffff0),
elevation: 10.0,
selectedIconTheme: IconThemeData(size: 1.0),
unselectedIconTheme: IconThemeData(size: 2.0),
selectedItemColor: Color(0xfffffff1),
unselectedItemColor: Color(0xfffffff2),
selectedLabelStyle: TextStyle(fontSize: 3.0),
unselectedLabelStyle: TextStyle(fontSize: 4.0),
showSelectedLabels: true,
showUnselectedLabels: true,
type: BottomNavigationBarType.fixed,
mouseCursor: MaterialStateMouseCursor.clickable,
).debugFillProperties(builder);
final List<String> description = builder.properties
.where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
.map((DiagnosticsNode node) => node.toString())
.toList();
expect(description[0], 'backgroundColor: Color(0xfffffff0)');
expect(description[1], 'elevation: 10.0');
// Ignore instance address for IconThemeData.
expect(description[2].contains('selectedIconTheme: IconThemeData'), isTrue);
expect(description[2].contains('(size: 1.0)'), isTrue);
expect(description[3].contains('unselectedIconTheme: IconThemeData'), isTrue);
expect(description[3].contains('(size: 2.0)'), isTrue);
expect(description[4], 'selectedItemColor: Color(0xfffffff1)');
expect(description[5], 'unselectedItemColor: Color(0xfffffff2)');
expect(description[6], 'selectedLabelStyle: TextStyle(inherit: true, size: 3.0)');
expect(description[7], 'unselectedLabelStyle: TextStyle(inherit: true, size: 4.0)');
expect(description[8], 'showSelectedLabels: true');
expect(description[9], 'showUnselectedLabels: true');
expect(description[10], 'type: BottomNavigationBarType.fixed');
expect(description[11], 'mouseCursor: MaterialStateMouseCursor(clickable)');
});
testWidgets('BottomNavigationBar is themeable', (WidgetTester tester) async {
const Color backgroundColor = Color(0xFF000001);
const Color selectedItemColor = Color(0xFF000002);
const Color unselectedItemColor = Color(0xFF000003);
const IconThemeData selectedIconTheme = IconThemeData(size: 10);
const IconThemeData unselectedIconTheme = IconThemeData(size: 11);
const TextStyle selectedTextStyle = TextStyle(fontSize: 22);
const TextStyle unselectedTextStyle = TextStyle(fontSize: 21);
const double elevation = 9.0;
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
bottomNavigationBarTheme: BottomNavigationBarThemeData(
backgroundColor: backgroundColor,
selectedItemColor: selectedItemColor,
unselectedItemColor: unselectedItemColor,
selectedIconTheme: selectedIconTheme,
unselectedIconTheme: unselectedIconTheme,
elevation: elevation,
showUnselectedLabels: true,
showSelectedLabels: true,
type: BottomNavigationBarType.fixed,
selectedLabelStyle: selectedTextStyle,
unselectedLabelStyle: unselectedTextStyle,
mouseCursor: MaterialStateProperty.resolveWith<MouseCursor?>((Set<MaterialState> states) {
if (states.contains(MaterialState.selected)) {
return SystemMouseCursors.grab;
}
return SystemMouseCursors.move;
}),
),
),
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
label: 'AC',
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
),
),
),
);
final Finder findACTransform = find.descendant(
of: find.byType(BottomNavigationBar),
matching: find.ancestor(
of: find.text('AC'),
matching: find.byType(Transform),
),
);
final Finder findAlarmTransform = find.descendant(
of: find.byType(BottomNavigationBar),
matching: find.ancestor(
of: find.text('Alarm'),
matching: find.byType(Transform),
),
);
final TextStyle selectedFontStyle = tester.renderObject<RenderParagraph>(find.text('AC')).text.style!;
final TextStyle selectedIcon = _iconStyle(tester, Icons.ac_unit);
final TextStyle unselectedIcon = _iconStyle(tester, Icons.access_alarm);
expect(selectedFontStyle.fontSize, selectedFontStyle.fontSize);
// Unselected label has a font size of 22 but is scaled down to be font size 21.
expect(
tester.firstWidget<Transform>(findAlarmTransform).transform,
equals(Matrix4.diagonal3(Vector3.all(unselectedTextStyle.fontSize! / selectedTextStyle.fontSize!))),
);
expect(selectedIcon.color, equals(selectedItemColor));
expect(selectedIcon.fontSize, equals(selectedIconTheme.size));
expect(unselectedIcon.color, equals(unselectedItemColor));
expect(unselectedIcon.fontSize, equals(unselectedIconTheme.size));
// There should not be any [Opacity] or [FadeTransition] widgets
// since showUnselectedLabels and showSelectedLabels are true.
final Finder findOpacity = find.descendant(
of: find.byType(BottomNavigationBar),
matching: find.byType(Opacity),
);
final Finder findFadeTransition = find.descendant(
of: find.byType(BottomNavigationBar),
matching: find.byType(FadeTransition),
);
expect(findOpacity, findsNothing);
expect(findFadeTransition, findsNothing);
expect(_material(tester).elevation, equals(elevation));
expect(_material(tester).color, equals(backgroundColor));
final Offset selectedBarItem = tester.getCenter(findACTransform);
final Offset unselectedBarItem = tester.getCenter(findAlarmTransform);
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(selectedBarItem);
await tester.pumpAndSettle();
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.grab);
await gesture.moveTo(unselectedBarItem);
await tester.pumpAndSettle();
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.move);
});
testWidgets('BottomNavigationBar properties are taken over the theme values', (WidgetTester tester) async {
const Color themeBackgroundColor = Color(0xFF000001);
const Color themeSelectedItemColor = Color(0xFF000002);
const Color themeUnselectedItemColor = Color(0xFF000003);
const IconThemeData themeSelectedIconTheme = IconThemeData(size: 10);
const IconThemeData themeUnselectedIconTheme = IconThemeData(size: 11);
const TextStyle themeSelectedTextStyle = TextStyle(fontSize: 22);
const TextStyle themeUnselectedTextStyle = TextStyle(fontSize: 21);
const double themeElevation = 9.0;
const BottomNavigationBarLandscapeLayout themeLandscapeLayout = BottomNavigationBarLandscapeLayout.centered;
const MaterialStateMouseCursor themeCursor = MaterialStateMouseCursor.clickable;
const Color backgroundColor = Color(0xFF000004);
const Color selectedItemColor = Color(0xFF000005);
const Color unselectedItemColor = Color(0xFF000006);
const IconThemeData selectedIconTheme = IconThemeData(size: 15);
const IconThemeData unselectedIconTheme = IconThemeData(size: 16);
const TextStyle selectedTextStyle = TextStyle(fontSize: 25);
const TextStyle unselectedTextStyle = TextStyle(fontSize: 26);
const double elevation = 7.0;
const BottomNavigationBarLandscapeLayout landscapeLayout = BottomNavigationBarLandscapeLayout.spread;
const MaterialStateMouseCursor cursor = MaterialStateMouseCursor.textable;
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: themeBackgroundColor,
selectedItemColor: themeSelectedItemColor,
unselectedItemColor: themeUnselectedItemColor,
selectedIconTheme: themeSelectedIconTheme,
unselectedIconTheme: themeUnselectedIconTheme,
elevation: themeElevation,
showUnselectedLabels: false,
showSelectedLabels: false,
type: BottomNavigationBarType.shifting,
selectedLabelStyle: themeSelectedTextStyle,
unselectedLabelStyle: themeUnselectedTextStyle,
landscapeLayout: themeLandscapeLayout,
mouseCursor: themeCursor,
),
),
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
backgroundColor: backgroundColor,
selectedItemColor: selectedItemColor,
unselectedItemColor: unselectedItemColor,
selectedIconTheme: selectedIconTheme,
unselectedIconTheme: unselectedIconTheme,
elevation: elevation,
showUnselectedLabels: true,
showSelectedLabels: true,
type: BottomNavigationBarType.fixed,
selectedLabelStyle: selectedTextStyle,
unselectedLabelStyle: unselectedTextStyle,
landscapeLayout: landscapeLayout,
mouseCursor: cursor,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
label: 'AC',
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
),
),
),
);
Finder findDescendantOfBottomNavigationBar(Finder finder) {
return find.descendant(
of: find.byType(BottomNavigationBar),
matching: finder,
);
}
final TextStyle selectedFontStyle = tester.renderObject<RenderParagraph>(find.text('AC')).text.style!;
final TextStyle selectedIcon = _iconStyle(tester, Icons.ac_unit);
final TextStyle unselectedIcon = _iconStyle(tester, Icons.access_alarm);
expect(selectedFontStyle.fontSize, selectedFontStyle.fontSize);
// Unselected label has a font size of 22 but is scaled down to be font size 21.
expect(
tester.firstWidget<Transform>(
findDescendantOfBottomNavigationBar(
find.ancestor(
of: find.text('Alarm'),
matching: find.byType(Transform),
),
),
).transform,
equals(Matrix4.diagonal3(Vector3.all(unselectedTextStyle.fontSize! / selectedTextStyle.fontSize!))),
);
expect(selectedIcon.color, equals(selectedItemColor));
expect(selectedIcon.fontSize, equals(selectedIconTheme.size));
expect(unselectedIcon.color, equals(unselectedItemColor));
expect(unselectedIcon.fontSize, equals(unselectedIconTheme.size));
// There should not be any [Opacity] or [FadeTransition] widgets
// since showUnselectedLabels and showSelectedLabels are true.
final Finder findOpacity = findDescendantOfBottomNavigationBar(
find.byType(Opacity),
);
final Finder findFadeTransition = findDescendantOfBottomNavigationBar(
find.byType(FadeTransition),
);
expect(findOpacity, findsNothing);
expect(findFadeTransition, findsNothing);
expect(_material(tester).elevation, equals(elevation));
expect(_material(tester).color, equals(backgroundColor));
final Offset barItem = tester.getCenter(
findDescendantOfBottomNavigationBar(
find.ancestor(
of: find.text('AC'),
matching: find.byType(Transform),
),
),
);
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
await gesture.moveTo(barItem);
await tester.pumpAndSettle();
expect(RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), SystemMouseCursors.text);
});
testWidgets('BottomNavigationBarTheme can be used to hide all labels', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/66738.
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
showSelectedLabels: false,
showUnselectedLabels: false,
),
),
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
label: 'AC',
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
),
),
),
);
final Finder findVisibility = find.descendant(
of: find.byType(BottomNavigationBar),
matching: find.byType(Visibility),
);
expect(findVisibility, findsNWidgets(2));
expect(tester.widget<Visibility>(findVisibility.at(0)).visible, false);
expect(tester.widget<Visibility>(findVisibility.at(1)).visible, false);
});
testWidgets('BottomNavigationBarTheme can be used to hide selected labels', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/66738.
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
showSelectedLabels: false,
showUnselectedLabels: true,
),
),
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
label: 'AC',
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
),
),
),
);
final Finder findFadeTransition = find.descendant(
of: find.byType(BottomNavigationBar),
matching: find.byType(FadeTransition),
);
expect(findFadeTransition, findsNWidgets(2));
expect(tester.widget<FadeTransition>(findFadeTransition.at(0)).opacity.value, 0.0);
expect(tester.widget<FadeTransition>(findFadeTransition.at(1)).opacity.value, 1.0);
});
testWidgets('BottomNavigationBarTheme can be used to hide unselected labels', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
showSelectedLabels: true,
showUnselectedLabels: false,
),
),
home: Scaffold(
bottomNavigationBar: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.ac_unit),
label: 'AC',
),
BottomNavigationBarItem(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
],
),
),
),
);
final Finder findFadeTransition = find.descendant(
of: find.byType(BottomNavigationBar),
matching: find.byType(FadeTransition),
);
expect(findFadeTransition, findsNWidgets(2));
expect(tester.widget<FadeTransition>(findFadeTransition.at(0)).opacity.value, 1.0);
expect(tester.widget<FadeTransition>(findFadeTransition.at(1)).opacity.value, 0.0);
});
}
TextStyle _iconStyle(WidgetTester tester, IconData icon) {
final RichText iconRichText = tester.widget<RichText>(
find.descendant(of: find.byIcon(icon), matching: find.byType(RichText)),
);
return iconRichText.text.style!;
}
Material _material(WidgetTester tester) {
return tester.firstWidget<Material>(
find.descendant(of: find.byType(BottomNavigationBar), matching: find.byType(Material)),
);
}