Revert "Remove BottomNavigationBarItem.title deprecation" (#91930)

diff --git a/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart b/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart
index a22eb8f..cd01a9b 100644
--- a/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart
+++ b/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart
@@ -254,6 +254,7 @@
       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 c40ccbb..7f0999f 100644
--- a/packages/flutter/lib/src/material/bottom_navigation_bar.dart
+++ b/packages/flutter/lib/src/material/bottom_navigation_bar.dart
@@ -185,8 +185,9 @@
   }) : 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 label',
+        'Every item must have a non-null title or label',
        ),
        assert(0 <= currentIndex && currentIndex < items.length),
        assert(elevation == null || elevation >= 0.0),
@@ -246,13 +247,13 @@
   final double iconSize;
 
   /// The color of the selected [BottomNavigationBarItem.icon] and
-  /// [BottomNavigationBarItem.label].
+  /// [BottomNavigationBarItem.title].
   ///
   /// If null then the [ThemeData.primaryColor] is used.
   final Color? selectedItemColor;
 
   /// The color of the unselected [BottomNavigationBarItem.icon] and
-  /// [BottomNavigationBarItem.label]s.
+  /// [BottomNavigationBarItem.title]s.
   ///
   /// If null then the [ThemeData.unselectedWidgetColor]'s color is used.
   final Color? unselectedItemColor;
@@ -691,7 +692,7 @@
           ),
         ),
         alignment: Alignment.bottomCenter,
-        child: Text(item.label!),
+        child: item.title ?? 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 df0dd80..2aadba7 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 @@
   final IconThemeData? unselectedIconTheme;
 
   /// The color of the selected [BottomNavigationBarItem.icon] and
-  /// [BottomNavigationBarItem.label].
+  /// [BottomNavigationBarItem.title].
   ///
   /// See [BottomNavigationBar.selectedItemColor].
   final Color? selectedItemColor;
 
   /// The color of the unselected [BottomNavigationBarItem.icon] and
-  /// [BottomNavigationBarItem.label]s.
+  /// [BottomNavigationBarItem.title]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 41f1819..9303d6b 100644
--- a/packages/flutter/lib/src/widgets/bottom_navigation_bar_item.dart
+++ b/packages/flutter/lib/src/widgets/bottom_navigation_bar_item.dart
@@ -24,11 +24,17 @@
   /// 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.
@@ -61,6 +67,15 @@
   ///  * [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 d8d781f..6bd33d4 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 @@
     semantics.dispose();
   });
 
-  testWidgets('Label of items should be nullable', (WidgetTester tester) async {
+  testWidgets('Title of items should be nullable', (WidgetTester tester) async {
     final MemoryImage iconProvider = MemoryImage(Uint8List.fromList(kTransparentImage));
     final List<int> itemsTapped = <int>[];
 
diff --git a/packages/flutter/test/material/bottom_navigation_bar_test.dart b/packages/flutter/test/material/bottom_navigation_bar_test.dart
index c71b62f..88cf921 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 @@
             items: const <BottomNavigationBarItem>[
               BottomNavigationBarItem(
                 icon: Icon(Icons.ac_unit),
-                label: 'AC',
+                title: Text('AC'),
               ),
               BottomNavigationBarItem(
                 icon: Icon(Icons.access_alarm),
-                label: 'Alarm',
+                title: Text('Alarm'),
               ),
             ],
             onTap: (int index) {
@@ -1031,11 +1031,11 @@
             bottomNavigationBar: BottomNavigationBar(
               items: const <BottomNavigationBarItem>[
                 BottomNavigationBarItem(
-                  label: 'A',
+                  title: Text('A'),
                   icon: Icon(Icons.ac_unit),
                 ),
                 BottomNavigationBarItem(
-                  label: 'B',
+                  title: Text('B'),
                   icon: Icon(Icons.battery_alert),
                 ),
               ],
@@ -1046,7 +1046,7 @@
     );
 
     final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
-    expect(box.size.height, equals(56.0));
+    expect(box.size.height, equals(66.0));
   });
 
   testWidgets('BottomNavigationBar does not grow with textScaleFactor when labels are provided', (WidgetTester tester) async {
@@ -1216,9 +1216,9 @@
     expect(find.byTooltip('C'), findsNothing);
   });
 
-  testWidgets('BottomNavigationBar limits width of tiles with long labels', (WidgetTester tester) async {
-    final String longTextA = List<String>.generate(100, (int index) => 'A').toString();
-    final String longTextB = List<String>.generate(100, (int index) => 'B').toString();
+  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'));
 
     await tester.pumpWidget(
       MaterialApp(
@@ -1226,11 +1226,11 @@
           bottomNavigationBar: BottomNavigationBar(
             items: <BottomNavigationBarItem>[
               BottomNavigationBarItem(
-                label: longTextA,
+                title: longTextA,
                 icon: const Icon(Icons.ac_unit),
               ),
               BottomNavigationBarItem(
-                label: longTextB,
+                title: longTextB,
                 icon: const Icon(Icons.battery_alert),
               ),
             ],
@@ -1242,9 +1242,9 @@
     final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
     expect(box.size.height, equals(kBottomNavigationBarHeight));
 
-    final RenderBox itemBoxA = tester.renderObject(find.text(longTextA));
+    final RenderBox itemBoxA = tester.renderObject(find.text(longTextA.data!));
     expect(itemBoxA.size, equals(const Size(400.0, 14.0)));
-    final RenderBox itemBoxB = tester.renderObject(find.text(longTextB));
+    final RenderBox itemBoxB = tester.renderObject(find.text(longTextB.data!));
     expect(itemBoxB.size, equals(const Size(400.0, 14.0)));
   });
 
@@ -1379,15 +1379,15 @@
           items: const <BottomNavigationBarItem>[
             BottomNavigationBarItem(
               icon: Icon(Icons.ac_unit),
-              label: 'AC',
+              title: Text('AC'),
             ),
             BottomNavigationBarItem(
               icon: Icon(Icons.access_alarm),
-              label: 'Alarm',
+              title: Text('Alarm'),
             ),
             BottomNavigationBarItem(
               icon: Icon(Icons.hot_tub),
-              label: 'Hot Tub',
+              title: Text('Hot Tub'),
             ),
           ],
         ),
@@ -1433,15 +1433,15 @@
           items: const <BottomNavigationBarItem>[
             BottomNavigationBarItem(
               icon: Icon(Icons.ac_unit),
-              label: 'AC',
+              title: Text('AC'),
             ),
             BottomNavigationBarItem(
               icon: Icon(Icons.access_alarm),
-              label: 'Alarm',
+              title: Text('Alarm'),
             ),
             BottomNavigationBarItem(
               icon: Icon(Icons.hot_tub),
-              label: 'Hot Tub',
+              title: Text('Hot Tub'),
             ),
           ],
         ),
@@ -1626,7 +1626,7 @@
     }
   });
 
-  testWidgets('BottomNavigationBar item label should not be nullable', (WidgetTester tester) async {
+  testWidgets('BottomNavigationBar item title should not be nullable', (WidgetTester tester) async {
     expect(() {
       MaterialApp(
         home: Scaffold(
@@ -1732,11 +1732,11 @@
           items: const <BottomNavigationBarItem>[
             BottomNavigationBarItem(
               icon: Icon(Icons.ac_unit),
-              label: 'Red',
+              title: Text('Red'),
             ),
             BottomNavigationBarItem(
               icon: Icon(Icons.access_alarm),
-              label: 'Green',
+              title: Text('Green'),
             ),
           ],
         ),
@@ -1775,11 +1775,11 @@
           items: const <BottomNavigationBarItem>[
             BottomNavigationBarItem(
               icon: Icon(Icons.ac_unit),
-              label: 'Red',
+              title: Text('Red'),
             ),
             BottomNavigationBarItem(
               icon: Icon(Icons.access_alarm),
-              label: 'Green',
+              title: Text('Green'),
             ),
           ],
         ),
@@ -1817,8 +1817,8 @@
             child: BottomNavigationBar(
               mouseCursor: SystemMouseCursors.text,
               items: const <BottomNavigationBarItem>[
-                BottomNavigationBarItem(icon: Icon(Icons.ac_unit), label: 'AC'),
-                BottomNavigationBarItem(icon: Icon(Icons.access_alarm), label: 'Alarm'),
+                BottomNavigationBarItem(icon: Icon(Icons.ac_unit), title: Text('AC')),
+                BottomNavigationBarItem(icon: Icon(Icons.access_alarm), title: Text('Alarm')),
               ],
             ),
           ),
@@ -1842,8 +1842,8 @@
             cursor: SystemMouseCursors.forbidden,
             child: BottomNavigationBar(
               items: const <BottomNavigationBarItem>[
-                BottomNavigationBarItem(icon: Icon(Icons.ac_unit), label: 'AC'),
-                BottomNavigationBarItem(icon: Icon(Icons.access_alarm), label: 'Alarm'),
+                BottomNavigationBarItem(icon: Icon(Icons.ac_unit), title: Text('AC')),
+                BottomNavigationBarItem(icon: Icon(Icons.access_alarm), title: Text('Alarm')),
               ],
             ),
           ),
@@ -1875,8 +1875,8 @@
             child: BottomNavigationBar(
               enableFeedback: enableFeedback,
               items: const <BottomNavigationBarItem>[
-                BottomNavigationBarItem(icon: Icon(Icons.ac_unit), label: 'AC'),
-                BottomNavigationBarItem(icon: Icon(Icons.access_alarm), label: 'Alarm'),
+                BottomNavigationBarItem(icon: Icon(Icons.ac_unit), title: Text('AC')),
+                BottomNavigationBarItem(icon: Icon(Icons.access_alarm), title: Text('Alarm')),
               ],
             ),
           ),
@@ -2015,7 +2015,9 @@
 
   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(
@@ -2026,11 +2028,11 @@
                 items: <BottomNavigationBarItem>[
                   BottomNavigationBarItem(
                     icon: SizedBox(key: icon0, width: 200, height: 10),
-                    label: 'Title0',
+                    title: SizedBox(key: title0, width: 200, height: 10),
                   ),
                   BottomNavigationBarItem(
                     icon: SizedBox(key: icon1, width: 200, height: 10),
-                    label: 'Title1',
+                    title: SizedBox(key: title1, width: 200, height: 10),
                   ),
                 ],
               ),
@@ -2045,24 +2047,25 @@
     // 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, 560.0);
-    expect(tester.getRect(find.text('Title0')).bottom, 584.0);
+    expect(tester.getRect(find.byKey(icon0)).top, 562);
+    expect(tester.getRect(find.byKey(title0)).bottom, 582);
 
-    // 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));
+    // 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));
   });
 
   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(
@@ -2074,11 +2077,11 @@
                 items: <BottomNavigationBarItem>[
                   BottomNavigationBarItem(
                     icon: SizedBox(key: icon0, width: 200, height: 10),
-                    label: 'Title0',
+                    title: SizedBox(key: title0, width: 200, height: 10),
                   ),
                   BottomNavigationBarItem(
                     icon: SizedBox(key: icon1, width: 200, height: 10),
-                    label: 'Title1',
+                    title: SizedBox(key: title1, width: 200, height: 10),
                   ),
                 ],
               ),
@@ -2091,23 +2094,22 @@
     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 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));
+    // 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));
   });
 
   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(
@@ -2119,11 +2121,11 @@
                 items: <BottomNavigationBarItem>[
                   BottomNavigationBarItem(
                     icon: SizedBox(key: icon0, width: 100, height: 20),
-                    label: 'Title0',
+                    title: SizedBox(key: title0, width: 100, height: 20),
                   ),
                   BottomNavigationBarItem(
                     icon: SizedBox(key: icon1, width: 100, height: 20),
-                    label: 'Title1',
+                    title: SizedBox(key: title1, width: 100, height: 20),
                   ),
                 ],
               ),
@@ -2137,12 +2139,12 @@
     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/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));
+    // 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));
   });
 }