| // 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 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)), |
| ); |
| } |