diff --git a/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart b/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart index cd01a9bb12..a22eb8ffa2 100644 --- a/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart +++ b/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart @@ -254,7 +254,6 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget { Expanded( child: Center(child: active ? item.activeIcon : item.icon), ), - if (item.title != null) item.title!, if (item.label != null) Text(item.label!), ]; } diff --git a/packages/flutter/lib/src/material/bottom_navigation_bar.dart b/packages/flutter/lib/src/material/bottom_navigation_bar.dart index 7f0999fd14..c40ccbb523 100644 --- a/packages/flutter/lib/src/material/bottom_navigation_bar.dart +++ b/packages/flutter/lib/src/material/bottom_navigation_bar.dart @@ -185,9 +185,8 @@ class BottomNavigationBar extends StatefulWidget { }) : assert(items != null), assert(items.length >= 2), assert( - items.every((BottomNavigationBarItem item) => item.title != null) || items.every((BottomNavigationBarItem item) => item.label != null), - 'Every item must have a non-null title or label', + 'Every item must have a non-null label', ), assert(0 <= currentIndex && currentIndex < items.length), assert(elevation == null || elevation >= 0.0), @@ -247,13 +246,13 @@ class BottomNavigationBar extends StatefulWidget { final double iconSize; /// The color of the selected [BottomNavigationBarItem.icon] and - /// [BottomNavigationBarItem.title]. + /// [BottomNavigationBarItem.label]. /// /// If null then the [ThemeData.primaryColor] is used. final Color? selectedItemColor; /// The color of the unselected [BottomNavigationBarItem.icon] and - /// [BottomNavigationBarItem.title]s. + /// [BottomNavigationBarItem.label]s. /// /// If null then the [ThemeData.unselectedWidgetColor]'s color is used. final Color? unselectedItemColor; @@ -692,7 +691,7 @@ class _Label extends StatelessWidget { ), ), alignment: Alignment.bottomCenter, - child: item.title ?? Text(item.label!), + child: Text(item.label!), ), ); diff --git a/packages/flutter/lib/src/material/bottom_navigation_bar_theme.dart b/packages/flutter/lib/src/material/bottom_navigation_bar_theme.dart index 2aadba725b..df0dd80484 100644 --- a/packages/flutter/lib/src/material/bottom_navigation_bar_theme.dart +++ b/packages/flutter/lib/src/material/bottom_navigation_bar_theme.dart @@ -78,13 +78,13 @@ class BottomNavigationBarThemeData with Diagnosticable { final IconThemeData? unselectedIconTheme; /// The color of the selected [BottomNavigationBarItem.icon] and - /// [BottomNavigationBarItem.title]. + /// [BottomNavigationBarItem.label]. /// /// See [BottomNavigationBar.selectedItemColor]. final Color? selectedItemColor; /// The color of the unselected [BottomNavigationBarItem.icon] and - /// [BottomNavigationBarItem.title]s. + /// [BottomNavigationBarItem.label]s. /// /// See [BottomNavigationBar.unselectedItemColor]. final Color? unselectedItemColor; diff --git a/packages/flutter/lib/src/widgets/bottom_navigation_bar_item.dart b/packages/flutter/lib/src/widgets/bottom_navigation_bar_item.dart index 9303d6b6a8..41f181992d 100644 --- a/packages/flutter/lib/src/widgets/bottom_navigation_bar_item.dart +++ b/packages/flutter/lib/src/widgets/bottom_navigation_bar_item.dart @@ -24,17 +24,11 @@ class BottomNavigationBarItem { /// The argument [icon] should not be null and the argument [label] should not be null when used in a Material Design's [BottomNavigationBar]. const BottomNavigationBarItem({ required this.icon, - @Deprecated( - 'Use "label" instead, as it allows for an improved text-scaling experience. ' - 'This feature was deprecated after v1.19.0.', - ) - this.title, this.label, Widget? activeIcon, this.backgroundColor, this.tooltip, }) : activeIcon = activeIcon ?? icon, - assert(label == null || title == null), assert(icon != null); /// The icon of the item. @@ -67,15 +61,6 @@ class BottomNavigationBarItem { /// * [BottomNavigationBarItem.icon], for a description of how to pair icons. final Widget activeIcon; - /// The title of the item. If the title is not provided only the icon will be shown when not used in a Material Design [BottomNavigationBar]. - /// - /// This field is deprecated, use [label] instead. - @Deprecated( - 'Use "label" instead, as it allows for an improved text-scaling experience. ' - 'This feature was deprecated after v1.19.0.', - ) - final Widget? title; - /// The text label for this [BottomNavigationBarItem]. /// /// This will be used to create a [Text] widget to put in the bottom navigation bar. diff --git a/packages/flutter/test/cupertino/bottom_tab_bar_test.dart b/packages/flutter/test/cupertino/bottom_tab_bar_test.dart index 6bd33d48e7..d8d781f02b 100644 --- a/packages/flutter/test/cupertino/bottom_tab_bar_test.dart +++ b/packages/flutter/test/cupertino/bottom_tab_bar_test.dart @@ -429,7 +429,7 @@ Future main() async { semantics.dispose(); }); - testWidgets('Title of items should be nullable', (WidgetTester tester) async { + testWidgets('Label of items should be nullable', (WidgetTester tester) async { final MemoryImage iconProvider = MemoryImage(Uint8List.fromList(kTransparentImage)); final List itemsTapped = []; diff --git a/packages/flutter/test/material/bottom_navigation_bar_test.dart b/packages/flutter/test/material/bottom_navigation_bar_test.dart index 88cf921ec8..c71b62fde7 100644 --- a/packages/flutter/test/material/bottom_navigation_bar_test.dart +++ b/packages/flutter/test/material/bottom_navigation_bar_test.dart @@ -28,11 +28,11 @@ void main() { items: const [ BottomNavigationBarItem( icon: Icon(Icons.ac_unit), - title: Text('AC'), + label: 'AC', ), BottomNavigationBarItem( icon: Icon(Icons.access_alarm), - title: Text('Alarm'), + label: 'Alarm', ), ], onTap: (int index) { @@ -1031,11 +1031,11 @@ void main() { bottomNavigationBar: BottomNavigationBar( items: const [ BottomNavigationBarItem( - title: Text('A'), + label: 'A', icon: Icon(Icons.ac_unit), ), BottomNavigationBarItem( - title: Text('B'), + label: 'B', icon: Icon(Icons.battery_alert), ), ], @@ -1046,7 +1046,7 @@ void main() { ); final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar)); - expect(box.size.height, equals(66.0)); + expect(box.size.height, equals(56.0)); }); testWidgets('BottomNavigationBar does not grow with textScaleFactor when labels are provided', (WidgetTester tester) async { @@ -1216,9 +1216,9 @@ void main() { expect(find.byTooltip('C'), findsNothing); }); - testWidgets('BottomNavigationBar limits width of tiles with long titles', (WidgetTester tester) async { - final Text longTextA = Text(''.padLeft(100, 'A')); - final Text longTextB = Text(''.padLeft(100, 'B')); + testWidgets('BottomNavigationBar limits width of tiles with long labels', (WidgetTester tester) async { + final String longTextA = List.generate(100, (int index) => 'A').toString(); + final String longTextB = List.generate(100, (int index) => 'B').toString(); await tester.pumpWidget( MaterialApp( @@ -1226,11 +1226,11 @@ void main() { bottomNavigationBar: BottomNavigationBar( items: [ BottomNavigationBarItem( - title: longTextA, + label: longTextA, icon: const Icon(Icons.ac_unit), ), BottomNavigationBarItem( - title: longTextB, + label: longTextB, icon: const Icon(Icons.battery_alert), ), ], @@ -1242,9 +1242,9 @@ void main() { final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar)); expect(box.size.height, equals(kBottomNavigationBarHeight)); - final RenderBox itemBoxA = tester.renderObject(find.text(longTextA.data!)); + final RenderBox itemBoxA = tester.renderObject(find.text(longTextA)); expect(itemBoxA.size, equals(const Size(400.0, 14.0))); - final RenderBox itemBoxB = tester.renderObject(find.text(longTextB.data!)); + final RenderBox itemBoxB = tester.renderObject(find.text(longTextB)); expect(itemBoxB.size, equals(const Size(400.0, 14.0))); }); @@ -1379,15 +1379,15 @@ void main() { items: const [ BottomNavigationBarItem( icon: Icon(Icons.ac_unit), - title: Text('AC'), + label: 'AC', ), BottomNavigationBarItem( icon: Icon(Icons.access_alarm), - title: Text('Alarm'), + label: 'Alarm', ), BottomNavigationBarItem( icon: Icon(Icons.hot_tub), - title: Text('Hot Tub'), + label: 'Hot Tub', ), ], ), @@ -1433,15 +1433,15 @@ void main() { items: const [ BottomNavigationBarItem( icon: Icon(Icons.ac_unit), - title: Text('AC'), + label: 'AC', ), BottomNavigationBarItem( icon: Icon(Icons.access_alarm), - title: Text('Alarm'), + label: 'Alarm', ), BottomNavigationBarItem( icon: Icon(Icons.hot_tub), - title: Text('Hot Tub'), + label: 'Hot Tub', ), ], ), @@ -1626,7 +1626,7 @@ void main() { } }); - testWidgets('BottomNavigationBar item title should not be nullable', (WidgetTester tester) async { + testWidgets('BottomNavigationBar item label should not be nullable', (WidgetTester tester) async { expect(() { MaterialApp( home: Scaffold( @@ -1732,11 +1732,11 @@ void main() { items: const [ BottomNavigationBarItem( icon: Icon(Icons.ac_unit), - title: Text('Red'), + label: 'Red', ), BottomNavigationBarItem( icon: Icon(Icons.access_alarm), - title: Text('Green'), + label: 'Green', ), ], ), @@ -1775,11 +1775,11 @@ void main() { items: const [ BottomNavigationBarItem( icon: Icon(Icons.ac_unit), - title: Text('Red'), + label: 'Red', ), BottomNavigationBarItem( icon: Icon(Icons.access_alarm), - title: Text('Green'), + label: 'Green', ), ], ), @@ -1817,8 +1817,8 @@ void main() { child: BottomNavigationBar( mouseCursor: SystemMouseCursors.text, items: const [ - BottomNavigationBarItem(icon: Icon(Icons.ac_unit), title: Text('AC')), - BottomNavigationBarItem(icon: Icon(Icons.access_alarm), title: Text('Alarm')), + BottomNavigationBarItem(icon: Icon(Icons.ac_unit), label: 'AC'), + BottomNavigationBarItem(icon: Icon(Icons.access_alarm), label: 'Alarm'), ], ), ), @@ -1842,8 +1842,8 @@ void main() { cursor: SystemMouseCursors.forbidden, child: BottomNavigationBar( items: const [ - BottomNavigationBarItem(icon: Icon(Icons.ac_unit), title: Text('AC')), - BottomNavigationBarItem(icon: Icon(Icons.access_alarm), title: Text('Alarm')), + BottomNavigationBarItem(icon: Icon(Icons.ac_unit), label: 'AC'), + BottomNavigationBarItem(icon: Icon(Icons.access_alarm), label: 'Alarm'), ], ), ), @@ -1875,8 +1875,8 @@ void main() { child: BottomNavigationBar( enableFeedback: enableFeedback, items: const [ - BottomNavigationBarItem(icon: Icon(Icons.ac_unit), title: Text('AC')), - BottomNavigationBarItem(icon: Icon(Icons.access_alarm), title: Text('Alarm')), + BottomNavigationBarItem(icon: Icon(Icons.ac_unit), label: 'AC'), + BottomNavigationBarItem(icon: Icon(Icons.access_alarm), label: 'Alarm'), ], ), ), @@ -2015,9 +2015,7 @@ void main() { testWidgets('BottomNavigationBar default layout', (WidgetTester tester) async { final Key icon0 = UniqueKey(); - final Key title0 = UniqueKey(); final Key icon1 = UniqueKey(); - final Key title1 = UniqueKey(); await tester.pumpWidget( MaterialApp( @@ -2028,11 +2026,11 @@ void main() { items: [ BottomNavigationBarItem( icon: SizedBox(key: icon0, width: 200, height: 10), - title: SizedBox(key: title0, width: 200, height: 10), + label: 'Title0', ), BottomNavigationBarItem( icon: SizedBox(key: icon1, width: 200, height: 10), - title: SizedBox(key: title1, width: 200, height: 10), + label: 'Title1', ), ], ), @@ -2047,25 +2045,24 @@ void main() { // The height of the navigation bar is kBottomNavigationBarHeight = 56 // The top of the navigation bar is 600 - 56 = 544 // The top and bottom of the selected item is defined by its centered icon/label column: - // top = 544 - (56 - (10 + 10)) / 2 = 562 + // top = 544 + ((56 - (10 + 10)) / 2) = 562 // bottom = top + 10 + 10 = 582 - expect(tester.getRect(find.byKey(icon0)).top, 562); - expect(tester.getRect(find.byKey(title0)).bottom, 582); + expect(tester.getRect(find.byKey(icon0)).top, 560.0); + expect(tester.getRect(find.text('Title0')).bottom, 584.0); - // The items are horizontal padded according to - // MainAxisAlignment.spaceBetween Left/right padding is 800 - (200 - // * 4) / 4 = 100. The layout of the unselected item's title is - // slightly different; not checking that here. - expect(tester.getRect(find.byKey(title0)), const Rect.fromLTRB(100, 572, 300, 582)); - expect(tester.getRect(find.byKey(icon0)), const Rect.fromLTRB(100, 562, 300, 572)); - expect(tester.getRect(find.byKey(icon1)), const Rect.fromLTRB(500, 562, 700, 572)); + // The items are padded horizontally according to + // MainAxisAlignment.spaceAround. Left/right padding is: + // 800 - (200 * 2) / 4 = 100 + // The layout of the unselected item's label is slightly different; not + // checking that here. + expect(tester.getRect(find.text('Title0')), const Rect.fromLTRB(158.0, 570.0, 242.0, 584.0)); + expect(tester.getRect(find.byKey(icon0)), const Rect.fromLTRB(100.0, 560.0, 300.0, 570.0)); + expect(tester.getRect(find.byKey(icon1)), const Rect.fromLTRB(500.0, 560.0, 700.0, 570.0)); }); testWidgets('BottomNavigationBar centered landscape layout', (WidgetTester tester) async { final Key icon0 = UniqueKey(); - final Key title0 = UniqueKey(); final Key icon1 = UniqueKey(); - final Key title1 = UniqueKey(); await tester.pumpWidget( MaterialApp( @@ -2077,11 +2074,11 @@ void main() { items: [ BottomNavigationBarItem( icon: SizedBox(key: icon0, width: 200, height: 10), - title: SizedBox(key: title0, width: 200, height: 10), + label: 'Title0', ), BottomNavigationBarItem( icon: SizedBox(key: icon1, width: 200, height: 10), - title: SizedBox(key: title1, width: 200, height: 10), + label: 'Title1', ), ], ), @@ -2094,22 +2091,23 @@ void main() { expect(tester.getSize(find.byType(BottomNavigationBar)), const Size(800, kBottomNavigationBarHeight)); expect(tester.getRect(find.byType(BottomNavigationBar)), const Rect.fromLTRB(0, 600 - kBottomNavigationBarHeight, 800, 600)); - // The items are laid out as in the default case, within width=600 + // The items are laid out as in the default case, within width = 600 // (the "portrait" width) and the result is centered with the - // landscape width=800. So item 0's left edges are (800 - 600) / 2 + - // (600 - 400) / 4 = 150. Item 1's right edge is 800 - 150 = - // 650. The layout of the unselected item's title is slightly - // different; not checking that here. - expect(tester.getRect(find.byKey(title0)), const Rect.fromLTRB(150.0, 572.0, 350.0, 582.0)); - expect(tester.getRect(find.byKey(icon0)), const Rect.fromLTRB(150, 562, 350, 572)); - expect(tester.getRect(find.byKey(icon1)), const Rect.fromLTRB(450, 562, 650, 572)); + // landscape width = 800. + // So item 0's left edges are: + // ((800 - 600) / 2) + ((600 - 400) / 4) = 150. + // Item 1's right edge is: + // 800 - 150 = 650 + // The layout of the unselected item's label is slightly different; not + // checking that here. + expect(tester.getRect(find.text('Title0')), const Rect.fromLTRB(208.0, 570.0, 292.0, 584.0)); + expect(tester.getRect(find.byKey(icon0)), const Rect.fromLTRB(150.0, 560.0, 350.0, 570.0)); + expect(tester.getRect(find.byKey(icon1)), const Rect.fromLTRB(450.0, 560.0, 650.0, 570.0)); }); testWidgets('BottomNavigationBar linear landscape layout', (WidgetTester tester) async { final Key icon0 = UniqueKey(); - final Key title0 = UniqueKey(); final Key icon1 = UniqueKey(); - final Key title1 = UniqueKey(); await tester.pumpWidget( MaterialApp( @@ -2121,11 +2119,11 @@ void main() { items: [ BottomNavigationBarItem( icon: SizedBox(key: icon0, width: 100, height: 20), - title: SizedBox(key: title0, width: 100, height: 20), + label: 'Title0', ), BottomNavigationBarItem( icon: SizedBox(key: icon1, width: 100, height: 20), - title: SizedBox(key: title1, width: 100, height: 20), + label: 'Title1', ), ], ), @@ -2139,12 +2137,12 @@ void main() { expect(tester.getRect(find.byType(BottomNavigationBar)), const Rect.fromLTRB(0, 600 - kBottomNavigationBarHeight, 800, 600)); // The items are laid out as in the default case except each - // item's icon/title is arranged in a row, with 8 pixels in - // between the icon and title. The layout of the unselected - // item's title is slightly different; not checking that here. - expect(tester.getRect(find.byKey(title0)), const Rect.fromLTRB(204, 562, 304, 582)); - expect(tester.getRect(find.byKey(icon0)), const Rect.fromLTRB(96, 562, 196, 582)); - expect(tester.getRect(find.byKey(icon1)), const Rect.fromLTRB(496, 562, 596, 582)); + // item's icon/label is arranged in a row, with 8 pixels in + // between the icon and label. The layout of the unselected + // item's label is slightly different; not checking that here. + expect(tester.getRect(find.text('Title0')), const Rect.fromLTRB(212.0, 565.0, 296.0, 579.0)); + expect(tester.getRect(find.byKey(icon0)), const Rect.fromLTRB(104.0, 562.0, 204.0, 582.0)); + expect(tester.getRect(find.byKey(icon1)), const Rect.fromLTRB(504.0, 562.0, 604.0, 582.0)); }); }