diff --git a/packages/flutter/lib/src/material/bottom_navigation_bar.dart b/packages/flutter/lib/src/material/bottom_navigation_bar.dart index 3e57788995c..8df5612eb3c 100644 --- a/packages/flutter/lib/src/material/bottom_navigation_bar.dart +++ b/packages/flutter/lib/src/material/bottom_navigation_bar.dart @@ -449,6 +449,7 @@ class _BottomNavigationTile extends StatelessWidget { this.item, this.animation, this.iconSize, { + super.key, this.onTap, this.labelColorTween, this.iconColorTween, @@ -1101,6 +1102,7 @@ class _BottomNavigationBarState extends State with TickerPr widget.items[i], _animations[i], widget.iconSize, + key: widget.items[i].key, selectedIconTheme: widget.useLegacyColorScheme ? widget.selectedIconTheme ?? bottomTheme.selectedIconTheme : effectiveSelectedIconTheme, unselectedIconTheme: widget.useLegacyColorScheme ? widget.unselectedIconTheme ?? bottomTheme.unselectedIconTheme : effectiveUnselectedIconTheme, selectedLabelStyle: effectiveSelectedLabelStyle, 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 6b41b5ca6b2..30bb768cf4b 100644 --- a/packages/flutter/lib/src/widgets/bottom_navigation_bar_item.dart +++ b/packages/flutter/lib/src/widgets/bottom_navigation_bar_item.dart @@ -23,6 +23,7 @@ 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({ + this.key, required this.icon, this.label, Widget? activeIcon, @@ -30,6 +31,14 @@ class BottomNavigationBarItem { this.tooltip, }) : activeIcon = activeIcon ?? icon; + /// A key to be passed through to the resultant widget. + /// + /// This allows the identification of different [BottomNavigationBarItem]s through their keys. + /// + /// When changing the number of bar items in response to a bar item being tapped, giving + /// each item a key will allow the inkwell / splash animation to be correctly positioned. + final Key? key; + /// The icon of the item. /// /// Typically the icon is an [Icon] or an [ImageIcon] widget. If another type diff --git a/packages/flutter/test/material/bottom_navigation_bar_test.dart b/packages/flutter/test/material/bottom_navigation_bar_test.dart index 9ae041321c6..a80164ac423 100644 --- a/packages/flutter/test/material/bottom_navigation_bar_test.dart +++ b/packages/flutter/test/material/bottom_navigation_bar_test.dart @@ -3174,6 +3174,34 @@ void main() { addTearDown(tester.view.resetPhysicalSize); }); + + testWidgets('BottomNavigationBar keys passed through', (WidgetTester tester) async { + const Key key1 = Key('key1'); + const Key key2 = Key('key2'); + + await tester.pumpWidget( + boilerplate( + textDirection: TextDirection.ltr, + bottomNavigationBar: BottomNavigationBar( + items: const [ + BottomNavigationBarItem( + key: key1, + icon: Icon(Icons.favorite_border), + label: 'Favorite', + ), + BottomNavigationBarItem( + key: key2, + icon: Icon(Icons.access_alarm), + label: 'Alarm', + ), + ], + ), + ), + ); + + expect(find.byKey(key1), findsOneWidget); + expect(find.byKey(key2), findsOneWidget); + }); } Widget boilerplate({ Widget? bottomNavigationBar, required TextDirection textDirection, bool? useMaterial3 }) {