`NavigationBar` improvements (#116992)

diff --git a/examples/api/lib/material/navigation_bar/navigation_bar.0.dart b/examples/api/lib/material/navigation_bar/navigation_bar.0.dart
index ac22535..41cca1b 100644
--- a/examples/api/lib/material/navigation_bar/navigation_bar.0.dart
+++ b/examples/api/lib/material/navigation_bar/navigation_bar.0.dart
@@ -6,10 +6,10 @@
 
 import 'package:flutter/material.dart';
 
-void main() => runApp(const ExampleApp());
+void main() => runApp(const NavigationBarApp());
 
-class ExampleApp extends StatelessWidget {
-  const ExampleApp({super.key});
+class NavigationBarApp extends StatelessWidget {
+  const NavigationBarApp({super.key});
 
   @override
   Widget build(BuildContext context) {
diff --git a/examples/api/lib/material/navigation_bar/navigation_bar.1.dart b/examples/api/lib/material/navigation_bar/navigation_bar.1.dart
index 42d21aa..06191a7 100644
--- a/examples/api/lib/material/navigation_bar/navigation_bar.1.dart
+++ b/examples/api/lib/material/navigation_bar/navigation_bar.1.dart
@@ -2,223 +2,93 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-/// Flutter code sample for [NavigationBar] with nested [Navigator] destinations.
+/// Flutter code sample for [NavigationBar].
 
 import 'package:flutter/material.dart';
 
-void main() {
-  runApp(const MaterialApp(home: Home()));
-}
+void main() => runApp(const NavigationBarApp());
 
-class Home extends StatefulWidget {
-  const Home({ super.key });
-
-  @override
-  State<Home> createState() => _HomeState();
-}
-
-class _HomeState extends State<Home> with TickerProviderStateMixin<Home> {
-  static const List<Destination> allDestinations = <Destination>[
-    Destination(0, 'Teal', Icons.home, Colors.teal),
-    Destination(1, 'Cyan', Icons.business, Colors.cyan),
-    Destination(2, 'Orange', Icons.school, Colors.orange),
-    Destination(3, 'Blue', Icons.flight, Colors.blue),
-  ];
-
-  late final List<GlobalKey<NavigatorState>> navigatorKeys;
-  late final List<GlobalKey> destinationKeys;
-  late final List<AnimationController> destinationFaders;
-  late final List<Widget> destinationViews;
-  int selectedIndex = 0;
-
-  AnimationController buildFaderController() {
-    final AnimationController controller =  AnimationController(vsync: this, duration: const Duration(milliseconds: 200));
-    controller.addStatusListener((AnimationStatus status) {
-      if (status == AnimationStatus.dismissed) {
-        setState(() { }); // Rebuild unselected destinations offstage.
-      }
-    });
-    return controller;
-  }
-
-  @override
-  void initState() {
-    super.initState();
-    navigatorKeys = List<GlobalKey<NavigatorState>>.generate(allDestinations.length, (int index) => GlobalKey()).toList();
-    destinationFaders = List<AnimationController>.generate(allDestinations.length, (int index) => buildFaderController()).toList();
-    destinationFaders[selectedIndex].value = 1.0;
-    destinationViews = allDestinations.map((Destination destination) {
-      return FadeTransition(
-        opacity: destinationFaders[destination.index].drive(CurveTween(curve: Curves.fastOutSlowIn)),
-        child: DestinationView(
-          destination: destination,
-          navigatorKey: navigatorKeys[destination.index],
-        )
-      );
-    }).toList();
-  }
-
-  @override
-  void dispose() {
-    for (final AnimationController controller in destinationFaders) {
-      controller.dispose();
-    }
-    super.dispose();
-  }
+class NavigationBarApp extends StatelessWidget {
+  const NavigationBarApp({super.key});
 
   @override
   Widget build(BuildContext context) {
-    return WillPopScope(
-      onWillPop: () async {
-        final NavigatorState navigator = navigatorKeys[selectedIndex].currentState!;
-        if (!navigator.canPop()) {
-          return true;
-        }
-        navigator.pop();
-        return false;
-      },
-      child: Scaffold(
-        body: SafeArea(
-          top: false,
-          child: Stack(
-            fit: StackFit.expand,
-            children: allDestinations.map((Destination destination) {
-              final int index = destination.index;
-              final Widget view = destinationViews[index];
-              if (index == selectedIndex) {
-                destinationFaders[index].forward();
-                return Offstage(offstage: false, child: view);
-              } else {
-                destinationFaders[index].reverse();
-                if (destinationFaders[index].isAnimating) {
-                  return IgnorePointer(child: view);
-                }
-                return Offstage(child: view);
-              }
-            }).toList(),
-          ),
-        ),
-        bottomNavigationBar: NavigationBar(
-          selectedIndex: selectedIndex,
-          onDestinationSelected: (int index) {
-            setState(() {
-              selectedIndex = index;
-            });
-          },
-          destinations: allDestinations.map((Destination destination) {
-            return NavigationDestination(
-              icon: Icon(destination.icon, color: destination.color),
-              label: destination.title,
-            );
-          }).toList(),
-        ),
-      ),
-    );
+    return const MaterialApp(home: NavigationExample());
   }
 }
 
-class Destination {
-  const Destination(this.index, this.title, this.icon, this.color);
-  final int index;
-  final String title;
-  final IconData icon;
-  final MaterialColor color;
+class NavigationExample extends StatefulWidget {
+  const NavigationExample({super.key});
+
+  @override
+  State<NavigationExample> createState() => _NavigationExampleState();
 }
 
-class RootPage extends StatelessWidget {
-  const RootPage({ super.key, required this.destination });
-
-  final Destination destination;
-
-  Widget _buildDialog(BuildContext context) {
-    return AlertDialog(
-      title: Text('${destination.title} AlertDialog'),
-      actions: <Widget>[
-        TextButton(
-          onPressed: () { Navigator.pop(context); },
-          child: const Text('OK'),
-        ),
-      ],
-    );
-  }
+class _NavigationExampleState extends State<NavigationExample> {
+  int currentPageIndex = 0;
+  NavigationDestinationLabelBehavior labelBehavior = NavigationDestinationLabelBehavior.alwaysShow;
 
   @override
   Widget build(BuildContext context) {
-    final TextStyle headlineSmall = Theme.of(context).textTheme.headlineSmall!;
-    final ButtonStyle buttonStyle = ElevatedButton.styleFrom(
-      backgroundColor: destination.color,
-      visualDensity: VisualDensity.comfortable,
-      padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
-      textStyle: headlineSmall,
-    );
-
     return Scaffold(
-      appBar: AppBar(
-        title: Text('${destination.title} RootPage - /'),
-        backgroundColor: destination.color,
+      bottomNavigationBar: NavigationBar(
+        labelBehavior: labelBehavior,
+        selectedIndex: currentPageIndex,
+        onDestinationSelected: (int index) {
+          setState(() {
+            currentPageIndex = index;
+          });
+        },
+        destinations: const <Widget>[
+          NavigationDestination(
+            icon: Icon(Icons.explore),
+            label: 'Explore',
+          ),
+          NavigationDestination(
+            icon: Icon(Icons.commute),
+            label: 'Commute',
+          ),
+          NavigationDestination(
+            selectedIcon: Icon(Icons.bookmark),
+            icon: Icon(Icons.bookmark_border),
+            label: 'Saved',
+          ),
+        ],
       ),
-      backgroundColor: destination.color[50],
       body: Center(
         child: Column(
-          mainAxisSize: MainAxisSize.min,
+          mainAxisAlignment: MainAxisAlignment.center,
           children: <Widget>[
-            ElevatedButton(
-              style: buttonStyle,
-              onPressed: () {
-                Navigator.pushNamed(context, '/list');
-              },
-              child: const Text('Push /list'),
-            ),
-            const SizedBox(height: 16),
-            ElevatedButton(
-              style: buttonStyle,
-              onPressed: () {
-                showDialog(
-                  context: context,
-                  useRootNavigator: false,
-                  builder: _buildDialog,
-                );
-              },
-              child: const Text('Local Dialog'),
-            ),
-            const SizedBox(height: 16),
-            ElevatedButton(
-              style: buttonStyle,
-              onPressed: () {
-                showDialog(
-                  context: context,
-                  useRootNavigator: true,
-                  builder: _buildDialog,
-                );
-              },
-              child: const Text('Root Dialog'),
-            ),
-            const SizedBox(height: 16),
-            Builder(
-              builder: (BuildContext context) {
-                return ElevatedButton(
-                  style: buttonStyle,
+            Text('Label behavior: ${labelBehavior.name}'),
+            const SizedBox(height: 10),
+            OverflowBar(
+              spacing: 10.0,
+              children: <Widget>[
+                ElevatedButton(
                   onPressed: () {
-                    showBottomSheet(
-                      context: context,
-                      builder: (BuildContext context) {
-                        return Container(
-                          padding: const EdgeInsets.all(16),
-                          width: double.infinity,
-                          child: Text(
-                            '${destination.title} BottomSheet\n'
-                            'Tap the back button to dismiss',
-                            style: headlineSmall,
-                            softWrap: true,
-                            textAlign: TextAlign.center,
-                          ),
-                        );
-                      },
-                    );
+                    setState(() {
+                      labelBehavior = NavigationDestinationLabelBehavior.alwaysShow;
+                    });
                   },
-                  child: const Text('Local BottomSheet'),
-                );
-              },
+                  child: const Text('alwaysShow'),
+                ),
+                ElevatedButton(
+                  onPressed: () {
+                    setState(() {
+                      labelBehavior = NavigationDestinationLabelBehavior.onlyShowSelected;
+                    });
+                  },
+                  child: const Text('onlyShowSelected'),
+                ),
+                ElevatedButton(
+                  onPressed: () {
+                    setState(() {
+                      labelBehavior = NavigationDestinationLabelBehavior.alwaysHide;
+                    });
+                  },
+                  child: const Text('alwaysHide'),
+                ),
+              ],
             ),
           ],
         ),
@@ -226,142 +96,3 @@
     );
   }
 }
-
-class ListPage extends StatelessWidget {
-  const ListPage({ super.key, required this.destination });
-
-  final Destination destination;
-
-  @override
-  Widget build(BuildContext context) {
-    const int itemCount = 50;
-    final ButtonStyle buttonStyle = OutlinedButton.styleFrom(
-      foregroundColor: destination.color,
-      fixedSize: const Size.fromHeight(128),
-      textStyle: Theme.of(context).textTheme.headlineSmall,
-    );
-    return Scaffold(
-      appBar: AppBar(
-        title: Text('${destination.title} ListPage - /list'),
-        backgroundColor: destination.color,
-      ),
-      backgroundColor: destination.color[50],
-      body: SizedBox.expand(
-        child: ListView.builder(
-          itemCount: itemCount,
-          itemBuilder: (BuildContext context, int index) {
-            return Padding(
-              padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
-              child: OutlinedButton(
-                style: buttonStyle.copyWith(
-                  backgroundColor: MaterialStatePropertyAll<Color>(
-                    Color.lerp(destination.color[100], Colors.white, index / itemCount)!
-                  ),
-                ),
-                onPressed: () {
-                  Navigator.pushNamed(context, '/text');
-                },
-                child: Text('Push /text [$index]'),
-              ),
-            );
-          },
-        ),
-      ),
-    );
-  }
-}
-
-class TextPage extends StatefulWidget {
-  const TextPage({ super.key, required this.destination });
-
-  final Destination destination;
-
-  @override
-  State<TextPage> createState() => _TextPageState();
-}
-
-class _TextPageState extends State<TextPage> {
-  late final TextEditingController textController;
-
-  @override
-  void initState() {
-    super.initState();
-    textController = TextEditingController(text: 'Sample Text');
-  }
-
-  @override
-  void dispose() {
-    textController.dispose();
-    super.dispose();
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    final ThemeData theme = Theme.of(context);
-    return Scaffold(
-      appBar: AppBar(
-        title: Text('${widget.destination.title} TextPage - /list/text'),
-        backgroundColor: widget.destination.color,
-      ),
-      backgroundColor: widget.destination.color[50],
-      body: Container(
-        padding: const EdgeInsets.all(32.0),
-        alignment: Alignment.center,
-        child: TextField(
-          controller: textController,
-          style: theme.primaryTextTheme.headlineMedium?.copyWith(
-            color: widget.destination.color,
-          ),
-          decoration: InputDecoration(
-            focusedBorder: UnderlineInputBorder(
-              borderSide: BorderSide(
-                color: widget.destination.color,
-                width: 3.0,
-              ),
-            ),
-          ),
-        ),
-      ),
-    );
-  }
-}
-
-class DestinationView extends StatefulWidget {
-  const DestinationView({
-    super.key,
-    required this.destination,
-    required this.navigatorKey,
-  });
-
-  final Destination destination;
-  final Key navigatorKey;
-
-  @override
-  State<DestinationView> createState() => _DestinationViewState();
-}
-
-class _DestinationViewState extends State<DestinationView> {
-  @override
-  Widget build(BuildContext context) {
-    return Navigator(
-      key: widget.navigatorKey,
-      onGenerateRoute: (RouteSettings settings) {
-        return MaterialPageRoute<void>(
-          settings: settings,
-          builder: (BuildContext context) {
-            switch(settings.name) {
-              case '/':
-                return RootPage(destination: widget.destination);
-              case '/list':
-                return ListPage(destination: widget.destination);
-              case '/text':
-                return TextPage(destination: widget.destination);
-            }
-            assert(false);
-            return const SizedBox();
-          },
-        );
-      },
-    );
-  }
-}
diff --git a/examples/api/lib/material/navigation_bar/navigation_bar.2.dart b/examples/api/lib/material/navigation_bar/navigation_bar.2.dart
new file mode 100644
index 0000000..42d21aa
--- /dev/null
+++ b/examples/api/lib/material/navigation_bar/navigation_bar.2.dart
@@ -0,0 +1,367 @@
+// 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.
+
+/// Flutter code sample for [NavigationBar] with nested [Navigator] destinations.
+
+import 'package:flutter/material.dart';
+
+void main() {
+  runApp(const MaterialApp(home: Home()));
+}
+
+class Home extends StatefulWidget {
+  const Home({ super.key });
+
+  @override
+  State<Home> createState() => _HomeState();
+}
+
+class _HomeState extends State<Home> with TickerProviderStateMixin<Home> {
+  static const List<Destination> allDestinations = <Destination>[
+    Destination(0, 'Teal', Icons.home, Colors.teal),
+    Destination(1, 'Cyan', Icons.business, Colors.cyan),
+    Destination(2, 'Orange', Icons.school, Colors.orange),
+    Destination(3, 'Blue', Icons.flight, Colors.blue),
+  ];
+
+  late final List<GlobalKey<NavigatorState>> navigatorKeys;
+  late final List<GlobalKey> destinationKeys;
+  late final List<AnimationController> destinationFaders;
+  late final List<Widget> destinationViews;
+  int selectedIndex = 0;
+
+  AnimationController buildFaderController() {
+    final AnimationController controller =  AnimationController(vsync: this, duration: const Duration(milliseconds: 200));
+    controller.addStatusListener((AnimationStatus status) {
+      if (status == AnimationStatus.dismissed) {
+        setState(() { }); // Rebuild unselected destinations offstage.
+      }
+    });
+    return controller;
+  }
+
+  @override
+  void initState() {
+    super.initState();
+    navigatorKeys = List<GlobalKey<NavigatorState>>.generate(allDestinations.length, (int index) => GlobalKey()).toList();
+    destinationFaders = List<AnimationController>.generate(allDestinations.length, (int index) => buildFaderController()).toList();
+    destinationFaders[selectedIndex].value = 1.0;
+    destinationViews = allDestinations.map((Destination destination) {
+      return FadeTransition(
+        opacity: destinationFaders[destination.index].drive(CurveTween(curve: Curves.fastOutSlowIn)),
+        child: DestinationView(
+          destination: destination,
+          navigatorKey: navigatorKeys[destination.index],
+        )
+      );
+    }).toList();
+  }
+
+  @override
+  void dispose() {
+    for (final AnimationController controller in destinationFaders) {
+      controller.dispose();
+    }
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return WillPopScope(
+      onWillPop: () async {
+        final NavigatorState navigator = navigatorKeys[selectedIndex].currentState!;
+        if (!navigator.canPop()) {
+          return true;
+        }
+        navigator.pop();
+        return false;
+      },
+      child: Scaffold(
+        body: SafeArea(
+          top: false,
+          child: Stack(
+            fit: StackFit.expand,
+            children: allDestinations.map((Destination destination) {
+              final int index = destination.index;
+              final Widget view = destinationViews[index];
+              if (index == selectedIndex) {
+                destinationFaders[index].forward();
+                return Offstage(offstage: false, child: view);
+              } else {
+                destinationFaders[index].reverse();
+                if (destinationFaders[index].isAnimating) {
+                  return IgnorePointer(child: view);
+                }
+                return Offstage(child: view);
+              }
+            }).toList(),
+          ),
+        ),
+        bottomNavigationBar: NavigationBar(
+          selectedIndex: selectedIndex,
+          onDestinationSelected: (int index) {
+            setState(() {
+              selectedIndex = index;
+            });
+          },
+          destinations: allDestinations.map((Destination destination) {
+            return NavigationDestination(
+              icon: Icon(destination.icon, color: destination.color),
+              label: destination.title,
+            );
+          }).toList(),
+        ),
+      ),
+    );
+  }
+}
+
+class Destination {
+  const Destination(this.index, this.title, this.icon, this.color);
+  final int index;
+  final String title;
+  final IconData icon;
+  final MaterialColor color;
+}
+
+class RootPage extends StatelessWidget {
+  const RootPage({ super.key, required this.destination });
+
+  final Destination destination;
+
+  Widget _buildDialog(BuildContext context) {
+    return AlertDialog(
+      title: Text('${destination.title} AlertDialog'),
+      actions: <Widget>[
+        TextButton(
+          onPressed: () { Navigator.pop(context); },
+          child: const Text('OK'),
+        ),
+      ],
+    );
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final TextStyle headlineSmall = Theme.of(context).textTheme.headlineSmall!;
+    final ButtonStyle buttonStyle = ElevatedButton.styleFrom(
+      backgroundColor: destination.color,
+      visualDensity: VisualDensity.comfortable,
+      padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
+      textStyle: headlineSmall,
+    );
+
+    return Scaffold(
+      appBar: AppBar(
+        title: Text('${destination.title} RootPage - /'),
+        backgroundColor: destination.color,
+      ),
+      backgroundColor: destination.color[50],
+      body: Center(
+        child: Column(
+          mainAxisSize: MainAxisSize.min,
+          children: <Widget>[
+            ElevatedButton(
+              style: buttonStyle,
+              onPressed: () {
+                Navigator.pushNamed(context, '/list');
+              },
+              child: const Text('Push /list'),
+            ),
+            const SizedBox(height: 16),
+            ElevatedButton(
+              style: buttonStyle,
+              onPressed: () {
+                showDialog(
+                  context: context,
+                  useRootNavigator: false,
+                  builder: _buildDialog,
+                );
+              },
+              child: const Text('Local Dialog'),
+            ),
+            const SizedBox(height: 16),
+            ElevatedButton(
+              style: buttonStyle,
+              onPressed: () {
+                showDialog(
+                  context: context,
+                  useRootNavigator: true,
+                  builder: _buildDialog,
+                );
+              },
+              child: const Text('Root Dialog'),
+            ),
+            const SizedBox(height: 16),
+            Builder(
+              builder: (BuildContext context) {
+                return ElevatedButton(
+                  style: buttonStyle,
+                  onPressed: () {
+                    showBottomSheet(
+                      context: context,
+                      builder: (BuildContext context) {
+                        return Container(
+                          padding: const EdgeInsets.all(16),
+                          width: double.infinity,
+                          child: Text(
+                            '${destination.title} BottomSheet\n'
+                            'Tap the back button to dismiss',
+                            style: headlineSmall,
+                            softWrap: true,
+                            textAlign: TextAlign.center,
+                          ),
+                        );
+                      },
+                    );
+                  },
+                  child: const Text('Local BottomSheet'),
+                );
+              },
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}
+
+class ListPage extends StatelessWidget {
+  const ListPage({ super.key, required this.destination });
+
+  final Destination destination;
+
+  @override
+  Widget build(BuildContext context) {
+    const int itemCount = 50;
+    final ButtonStyle buttonStyle = OutlinedButton.styleFrom(
+      foregroundColor: destination.color,
+      fixedSize: const Size.fromHeight(128),
+      textStyle: Theme.of(context).textTheme.headlineSmall,
+    );
+    return Scaffold(
+      appBar: AppBar(
+        title: Text('${destination.title} ListPage - /list'),
+        backgroundColor: destination.color,
+      ),
+      backgroundColor: destination.color[50],
+      body: SizedBox.expand(
+        child: ListView.builder(
+          itemCount: itemCount,
+          itemBuilder: (BuildContext context, int index) {
+            return Padding(
+              padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
+              child: OutlinedButton(
+                style: buttonStyle.copyWith(
+                  backgroundColor: MaterialStatePropertyAll<Color>(
+                    Color.lerp(destination.color[100], Colors.white, index / itemCount)!
+                  ),
+                ),
+                onPressed: () {
+                  Navigator.pushNamed(context, '/text');
+                },
+                child: Text('Push /text [$index]'),
+              ),
+            );
+          },
+        ),
+      ),
+    );
+  }
+}
+
+class TextPage extends StatefulWidget {
+  const TextPage({ super.key, required this.destination });
+
+  final Destination destination;
+
+  @override
+  State<TextPage> createState() => _TextPageState();
+}
+
+class _TextPageState extends State<TextPage> {
+  late final TextEditingController textController;
+
+  @override
+  void initState() {
+    super.initState();
+    textController = TextEditingController(text: 'Sample Text');
+  }
+
+  @override
+  void dispose() {
+    textController.dispose();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final ThemeData theme = Theme.of(context);
+    return Scaffold(
+      appBar: AppBar(
+        title: Text('${widget.destination.title} TextPage - /list/text'),
+        backgroundColor: widget.destination.color,
+      ),
+      backgroundColor: widget.destination.color[50],
+      body: Container(
+        padding: const EdgeInsets.all(32.0),
+        alignment: Alignment.center,
+        child: TextField(
+          controller: textController,
+          style: theme.primaryTextTheme.headlineMedium?.copyWith(
+            color: widget.destination.color,
+          ),
+          decoration: InputDecoration(
+            focusedBorder: UnderlineInputBorder(
+              borderSide: BorderSide(
+                color: widget.destination.color,
+                width: 3.0,
+              ),
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+}
+
+class DestinationView extends StatefulWidget {
+  const DestinationView({
+    super.key,
+    required this.destination,
+    required this.navigatorKey,
+  });
+
+  final Destination destination;
+  final Key navigatorKey;
+
+  @override
+  State<DestinationView> createState() => _DestinationViewState();
+}
+
+class _DestinationViewState extends State<DestinationView> {
+  @override
+  Widget build(BuildContext context) {
+    return Navigator(
+      key: widget.navigatorKey,
+      onGenerateRoute: (RouteSettings settings) {
+        return MaterialPageRoute<void>(
+          settings: settings,
+          builder: (BuildContext context) {
+            switch(settings.name) {
+              case '/':
+                return RootPage(destination: widget.destination);
+              case '/list':
+                return ListPage(destination: widget.destination);
+              case '/text':
+                return TextPage(destination: widget.destination);
+            }
+            assert(false);
+            return const SizedBox();
+          },
+        );
+      },
+    );
+  }
+}
diff --git a/examples/api/test/material/navigation_bar/navigation_bar.0_test.dart b/examples/api/test/material/navigation_bar/navigation_bar.0_test.dart
index 501058e..b786889 100644
--- a/examples/api/test/material/navigation_bar/navigation_bar.0_test.dart
+++ b/examples/api/test/material/navigation_bar/navigation_bar.0_test.dart
@@ -11,10 +11,9 @@
   testWidgets('Navigation bar updates destination on tap',
       (WidgetTester tester) async {
     await tester.pumpWidget(
-      const example.ExampleApp(),
+      const example.NavigationBarApp(),
     );
-    final NavigationBar navigationBarWidget =
-        tester.firstWidget(find.byType(NavigationBar));
+    final NavigationBar navigationBarWidget = tester.firstWidget(find.byType(NavigationBar));
 
     /// NavigationDestinations must be rendered
     expect(find.text('Explore'), findsOneWidget);
diff --git a/examples/api/test/material/navigation_bar/navigation_bar.1_test.dart b/examples/api/test/material/navigation_bar/navigation_bar.1_test.dart
index 23c35f9..1300565 100644
--- a/examples/api/test/material/navigation_bar/navigation_bar.1_test.dart
+++ b/examples/api/test/material/navigation_bar/navigation_bar.1_test.dart
@@ -3,106 +3,41 @@
 // found in the LICENSE file.
 
 import 'package:flutter/material.dart';
-import 'package:flutter_api_samples/material/navigation_bar/navigation_bar.1.dart' as example;
+import 'package:flutter_api_samples/material/navigation_bar/navigation_bar.1.dart'
+    as example;
 import 'package:flutter_test/flutter_test.dart';
 
 void main() {
-  testWidgets('RootPage: only selected destination is on stage', (WidgetTester tester) async {
-    await tester.pumpWidget(const MaterialApp(home: example.Home()));
+  testWidgets('Navigation bar updates label behavior when tapping buttons',
+      (WidgetTester tester) async {
+    await tester.pumpWidget(
+      const example.NavigationBarApp(),
+    );
+    NavigationBar navigationBarWidget = tester.firstWidget(find.byType(NavigationBar));
 
-    const String tealTitle = 'Teal RootPage - /';
-    const String cyanTitle = 'Cyan RootPage - /';
-    const String orangeTitle = 'Orange RootPage - /';
-    const String blueTitle = 'Blue RootPage - /';
+    expect(find.text('Label behavior: alwaysShow'), findsOneWidget);
 
-    await tester.tap(find.widgetWithText(NavigationDestination, 'Teal'));
+    /// Test alwaysShow label behavior button.
+    await tester.tap(find.widgetWithText(ElevatedButton, 'alwaysShow'));
     await tester.pumpAndSettle();
-    expect(find.text(tealTitle), findsOneWidget);
-    expect(find.text(cyanTitle), findsNothing);
-    expect(find.text(orangeTitle), findsNothing);
-    expect(find.text(blueTitle), findsNothing);
 
-    await tester.tap(find.widgetWithText(NavigationDestination, 'Cyan'));
-    await tester.pumpAndSettle();
-    expect(find.text(tealTitle), findsNothing);
-    expect(find.text(cyanTitle), findsOneWidget);
-    expect(find.text(orangeTitle), findsNothing);
-    expect(find.text(blueTitle), findsNothing);
+    expect(find.text('Label behavior: alwaysShow'), findsOneWidget);
+    expect(navigationBarWidget.labelBehavior, NavigationDestinationLabelBehavior.alwaysShow);
 
-    await tester.tap(find.widgetWithText(NavigationDestination, 'Orange'));
+    /// Test onlyShowSelected label behavior button.
+    await tester.tap(find.widgetWithText(ElevatedButton, 'onlyShowSelected'));
     await tester.pumpAndSettle();
-    expect(find.text(tealTitle), findsNothing);
-    expect(find.text(cyanTitle), findsNothing);
-    expect(find.text(orangeTitle), findsOneWidget);
-    expect(find.text(blueTitle), findsNothing);
 
-    await tester.tap(find.widgetWithText(NavigationDestination, 'Blue'));
-    await tester.pumpAndSettle();
-    expect(find.text(tealTitle), findsNothing);
-    expect(find.text(cyanTitle), findsNothing);
-    expect(find.text(orangeTitle), findsNothing);
-    expect(find.text(blueTitle), findsOneWidget);
-  });
+    expect(find.text('Label behavior: onlyShowSelected'), findsOneWidget);
+    navigationBarWidget = tester.firstWidget(find.byType(NavigationBar));
+    expect(navigationBarWidget.labelBehavior, NavigationDestinationLabelBehavior.onlyShowSelected);
 
-  testWidgets('RootPage', (WidgetTester tester) async {
-    await tester.pumpWidget(const MaterialApp(home: example.Home()));
+    /// Test alwaysHide label behavior button.
+    await tester.tap(find.widgetWithText(ElevatedButton, 'alwaysHide'));
+    await tester.pumpAndSettle();
 
-    await tester.tap(find.widgetWithText(NavigationDestination, 'Teal'));
-    await tester.pumpAndSettle();
-    await tester.tap(find.text('Local Dialog'));
-    await tester.pumpAndSettle();
-    expect(find.text('Teal AlertDialog'), findsOneWidget);
-    await tester.tap(find.text('OK'));
-    await tester.pumpAndSettle();
-    expect(find.text('Teal AlertDialog'), findsNothing);
-
-    await tester.pumpAndSettle();
-    await tester.tap(find.text('Root Dialog'));
-    await tester.pumpAndSettle();
-    expect(find.text('Teal AlertDialog'), findsOneWidget);
-    await tester.tapAt(const Offset(5, 5));
-    await tester.pumpAndSettle();
-    expect(find.text('Teal AlertDialog'), findsNothing);
-
-    await tester.tap(find.text('Local BottomSheet'));
-    await tester.pumpAndSettle();
-    expect(find.byType(BottomSheet), findsOneWidget);
-    await tester.tap(find.byType(BackButton));
-    await tester.pumpAndSettle();
-    expect(find.byType(BottomSheet), findsNothing);
-
-    await tester.tap(find.text('Push /list'));
-    await tester.pumpAndSettle();
-    expect(find.text('Teal ListPage - /list'), findsOneWidget);
-  });
-
-
-  testWidgets('ListPage', (WidgetTester tester) async {
-    await tester.pumpWidget(const MaterialApp(home: example.Home()));
-    expect(find.text('Teal RootPage - /'), findsOneWidget);
-
-    await tester.tap(find.widgetWithText(ElevatedButton, 'Push /list'));
-    await tester.pumpAndSettle();
-    expect(find.text('Teal ListPage - /list'), findsOneWidget);
-    expect(find.text('Push /text [0]'), findsOneWidget);
-
-    await tester.tap(find.widgetWithText(NavigationDestination, 'Orange'));
-    await tester.pumpAndSettle();
-    await tester.tap(find.widgetWithText(ElevatedButton, 'Push /list'));
-    await tester.pumpAndSettle();
-    expect(find.text('Orange ListPage - /list'), findsOneWidget);
-    expect(find.text('Push /text [0]'), findsOneWidget);
-
-    await tester.tap(find.byType(BackButton));
-    await tester.pumpAndSettle();
-    expect(find.text('Orange RootPage - /'), findsOneWidget);
-
-    await tester.tap(find.widgetWithText(NavigationDestination, 'Teal'));
-    await tester.pumpAndSettle();
-    expect(find.text('Teal ListPage - /list'), findsOneWidget);
-
-    await tester.tap(find.byType(BackButton));
-    await tester.pumpAndSettle();
-    expect(find.text('Teal RootPage - /'), findsOneWidget);
+    expect(find.text('Label behavior: alwaysHide'), findsOneWidget);
+    navigationBarWidget = tester.firstWidget(find.byType(NavigationBar));
+    expect(navigationBarWidget.labelBehavior, NavigationDestinationLabelBehavior.alwaysHide);
   });
 }
diff --git a/examples/api/test/material/navigation_bar/navigation_bar.2_test.dart b/examples/api/test/material/navigation_bar/navigation_bar.2_test.dart
new file mode 100644
index 0000000..cba7381
--- /dev/null
+++ b/examples/api/test/material/navigation_bar/navigation_bar.2_test.dart
@@ -0,0 +1,108 @@
+// 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/material.dart';
+import 'package:flutter_api_samples/material/navigation_bar/navigation_bar.2.dart' as example;
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  testWidgets('RootPage: only selected destination is on stage', (WidgetTester tester) async {
+    await tester.pumpWidget(const MaterialApp(home: example.Home()));
+
+    const String tealTitle = 'Teal RootPage - /';
+    const String cyanTitle = 'Cyan RootPage - /';
+    const String orangeTitle = 'Orange RootPage - /';
+    const String blueTitle = 'Blue RootPage - /';
+
+    await tester.tap(find.widgetWithText(NavigationDestination, 'Teal'));
+    await tester.pumpAndSettle();
+    expect(find.text(tealTitle), findsOneWidget);
+    expect(find.text(cyanTitle), findsNothing);
+    expect(find.text(orangeTitle), findsNothing);
+    expect(find.text(blueTitle), findsNothing);
+
+    await tester.tap(find.widgetWithText(NavigationDestination, 'Cyan'));
+    await tester.pumpAndSettle();
+    expect(find.text(tealTitle), findsNothing);
+    expect(find.text(cyanTitle), findsOneWidget);
+    expect(find.text(orangeTitle), findsNothing);
+    expect(find.text(blueTitle), findsNothing);
+
+    await tester.tap(find.widgetWithText(NavigationDestination, 'Orange'));
+    await tester.pumpAndSettle();
+    expect(find.text(tealTitle), findsNothing);
+    expect(find.text(cyanTitle), findsNothing);
+    expect(find.text(orangeTitle), findsOneWidget);
+    expect(find.text(blueTitle), findsNothing);
+
+    await tester.tap(find.widgetWithText(NavigationDestination, 'Blue'));
+    await tester.pumpAndSettle();
+    expect(find.text(tealTitle), findsNothing);
+    expect(find.text(cyanTitle), findsNothing);
+    expect(find.text(orangeTitle), findsNothing);
+    expect(find.text(blueTitle), findsOneWidget);
+  });
+
+  testWidgets('RootPage', (WidgetTester tester) async {
+    await tester.pumpWidget(const MaterialApp(home: example.Home()));
+
+    await tester.tap(find.widgetWithText(NavigationDestination, 'Teal'));
+    await tester.pumpAndSettle();
+    await tester.tap(find.text('Local Dialog'));
+    await tester.pumpAndSettle();
+    expect(find.text('Teal AlertDialog'), findsOneWidget);
+    await tester.tap(find.text('OK'));
+    await tester.pumpAndSettle();
+    expect(find.text('Teal AlertDialog'), findsNothing);
+
+    await tester.pumpAndSettle();
+    await tester.tap(find.text('Root Dialog'));
+    await tester.pumpAndSettle();
+    expect(find.text('Teal AlertDialog'), findsOneWidget);
+    await tester.tapAt(const Offset(5, 5));
+    await tester.pumpAndSettle();
+    expect(find.text('Teal AlertDialog'), findsNothing);
+
+    await tester.tap(find.text('Local BottomSheet'));
+    await tester.pumpAndSettle();
+    expect(find.byType(BottomSheet), findsOneWidget);
+    await tester.tap(find.byType(BackButton));
+    await tester.pumpAndSettle();
+    expect(find.byType(BottomSheet), findsNothing);
+
+    await tester.tap(find.text('Push /list'));
+    await tester.pumpAndSettle();
+    expect(find.text('Teal ListPage - /list'), findsOneWidget);
+  });
+
+
+  testWidgets('ListPage', (WidgetTester tester) async {
+    await tester.pumpWidget(const MaterialApp(home: example.Home()));
+    expect(find.text('Teal RootPage - /'), findsOneWidget);
+
+    await tester.tap(find.widgetWithText(ElevatedButton, 'Push /list'));
+    await tester.pumpAndSettle();
+    expect(find.text('Teal ListPage - /list'), findsOneWidget);
+    expect(find.text('Push /text [0]'), findsOneWidget);
+
+    await tester.tap(find.widgetWithText(NavigationDestination, 'Orange'));
+    await tester.pumpAndSettle();
+    await tester.tap(find.widgetWithText(ElevatedButton, 'Push /list'));
+    await tester.pumpAndSettle();
+    expect(find.text('Orange ListPage - /list'), findsOneWidget);
+    expect(find.text('Push /text [0]'), findsOneWidget);
+
+    await tester.tap(find.byType(BackButton));
+    await tester.pumpAndSettle();
+    expect(find.text('Orange RootPage - /'), findsOneWidget);
+
+    await tester.tap(find.widgetWithText(NavigationDestination, 'Teal'));
+    await tester.pumpAndSettle();
+    expect(find.text('Teal ListPage - /list'), findsOneWidget);
+
+    await tester.tap(find.byType(BackButton));
+    await tester.pumpAndSettle();
+    expect(find.text('Teal RootPage - /'), findsOneWidget);
+  });
+}
diff --git a/packages/flutter/lib/src/material/navigation_bar.dart b/packages/flutter/lib/src/material/navigation_bar.dart
index 0b24712..b17704b 100644
--- a/packages/flutter/lib/src/material/navigation_bar.dart
+++ b/packages/flutter/lib/src/material/navigation_bar.dart
@@ -53,6 +53,14 @@
 /// {@end-tool}
 ///
 /// {@tool dartpad}
+/// This example showcases [NavigationBar] label behaviors. When tapping on one
+/// of the label behavior options, the [labelBehavior] of the [NavigationBar]
+/// will be updated.
+///
+/// ** See code in examples/api/lib/material/navigation_bar/navigation_bar.1.dart **
+/// {@end-tool}
+///
+/// {@tool dartpad}
 /// This example shows a [NavigationBar] as it is used within a [Scaffold]
 /// widget when there are nested navigators that provide local navigation. The
 /// [NavigationBar] has four [NavigationDestination] widgets with different
@@ -60,7 +68,7 @@
 /// item's index and displays a corresponding page with its own local navigator
 /// in the body of a [Scaffold].
 ///
-/// ** See code in examples/api/lib/material/navigation_bar/navigation_bar.1.dart **
+/// ** See code in examples/api/lib/material/navigation_bar/navigation_bar.2.dart **
 /// {@end-tool}
 /// See also:
 ///
@@ -266,6 +274,8 @@
     super.key,
     required this.icon,
     this.selectedIcon,
+    this.indicatorColor,
+    this.indicatorShape,
     required this.label,
     this.tooltip,
   });
@@ -290,6 +300,12 @@
   /// would use a size of 24.0 and [ColorScheme.onSurface].
   final Widget? selectedIcon;
 
+  /// The color of the [indicatorShape] when this destination is selected.
+  final Color? indicatorColor;
+
+  /// The shape of the selected inidicator.
+  final ShapeBorder? indicatorShape;
+
   /// The text label that appears below the icon of this
   /// [NavigationDestination].
   ///
@@ -335,8 +351,8 @@
           children: <Widget>[
             NavigationIndicator(
               animation: animation,
-              color: navigationBarTheme.indicatorColor ?? defaults.indicatorColor!,
-              shape: navigationBarTheme.indicatorShape ?? defaults.indicatorShape!
+              color: indicatorColor ?? navigationBarTheme.indicatorColor ?? defaults.indicatorColor!,
+              shape: indicatorShape ?? navigationBarTheme.indicatorShape ?? defaults.indicatorShape!
             ),
             _StatusTransitionWidgetBuilder(
               animation: animation,
@@ -440,10 +456,10 @@
     final double labelPadding;
     switch (info.labelBehavior) {
       case NavigationDestinationLabelBehavior.alwaysShow:
-        labelPadding = 10;
+        labelPadding = 8;
         break;
       case NavigationDestinationLabelBehavior.onlyShowSelected:
-        labelPadding = selected ? 10 : 0;
+        labelPadding = selected ? 8 : 0;
         break;
       case NavigationDestinationLabelBehavior.alwaysHide:
         labelPadding = 0;
diff --git a/packages/flutter/test/material/navigation_bar_test.dart b/packages/flutter/test/material/navigation_bar_test.dart
index 17028c6..cba5d37 100644
--- a/packages/flutter/test/material/navigation_bar_test.dart
+++ b/packages/flutter/test/material/navigation_bar_test.dart
@@ -589,7 +589,7 @@
     await tester.pumpAndSettle();
 
     final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures');
-    Offset indicatorCenter = const Offset(600, 30);
+    Offset indicatorCenter = const Offset(600, 32);
     const Size includedIndicatorSize = Size(64, 32);
     const Size excludedIndicatorSize = Size(74, 40);
 
@@ -715,7 +715,7 @@
     selectedIndex = 1;
     await tester.pumpWidget(buildWidget(labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected));
     await tester.pumpAndSettle();
-    indicatorCenter = const Offset(600, 30);
+    indicatorCenter = const Offset(600, 32);
 
     expect(
       inkFeatures,
@@ -803,6 +803,96 @@
     transform = tester.widget<Transform>(transformFinder).transform;
     expect(transform.getColumn(0)[0], 1.0);
   });
+
+  testWidgets('Navigation destination updates indicator color and shape', (WidgetTester tester) async {
+    final ThemeData theme = ThemeData(useMaterial3: true);
+    const Color color = Color(0xff0000ff);
+    const ShapeBorder shape = CircleBorder();
+
+    Widget buildNaviagationBar({Color? indicatorColor, ShapeBorder? indicatorShape}) {
+      return MaterialApp(
+        theme: theme,
+        home: Scaffold(
+          bottomNavigationBar: NavigationBar(
+            destinations: <Widget>[
+              NavigationDestination(
+                icon: const Icon(Icons.ac_unit),
+                label: 'AC',
+                indicatorColor: indicatorColor,
+                indicatorShape: indicatorShape,
+              ),
+              const NavigationDestination(
+                icon: Icon(Icons.access_alarm),
+                label: 'Alarm',
+              ),
+            ],
+            onDestinationSelected: (int i) { },
+          ),
+        ),
+      );
+    }
+
+    await tester.pumpWidget(buildNaviagationBar());
+
+    // Test default indicator color and shape.
+    expect(_indicator(tester)?.color, theme.colorScheme.secondaryContainer);
+    expect(_indicator(tester)?.shape, const StadiumBorder());
+
+    await tester.pumpWidget(buildNaviagationBar(indicatorColor: color, indicatorShape: shape));
+
+    // Test custom indicator color and shape.
+    expect(_indicator(tester)?.color, color);
+    expect(_indicator(tester)?.shape, shape);
+  });
+
+  group('Material 2', () {
+    // Tests that are only relevant for Material 2. Once ThemeData.useMaterial3
+    // is turned on by default, these tests can be removed.
+
+    testWidgets('Navigation destination updates indicator color and shape', (WidgetTester tester) async {
+      final ThemeData theme = ThemeData(useMaterial3: false);
+      const Color color = Color(0xff0000ff);
+      const ShapeBorder shape = CircleBorder();
+
+      Widget buildNaviagationBar({Color? indicatorColor, ShapeBorder? indicatorShape}) {
+        return MaterialApp(
+          theme: theme,
+          home: Scaffold(
+            bottomNavigationBar: NavigationBar(
+              destinations: <Widget>[
+                NavigationDestination(
+                  icon: const Icon(Icons.ac_unit),
+                  label: 'AC',
+                  indicatorColor: indicatorColor,
+                  indicatorShape: indicatorShape,
+                ),
+                const NavigationDestination(
+                  icon: Icon(Icons.access_alarm),
+                  label: 'Alarm',
+                ),
+              ],
+              onDestinationSelected: (int i) { },
+            ),
+          ),
+        );
+      }
+
+      await tester.pumpWidget(buildNaviagationBar());
+
+      // Test default indicator color and shape.
+      expect(_indicator(tester)?.color, theme.colorScheme.secondary.withOpacity(0.24));
+      expect(
+        _indicator(tester)?.shape,
+        const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
+      );
+
+      await tester.pumpWidget(buildNaviagationBar(indicatorColor: color, indicatorShape: shape));
+
+      // Test custom indicator color and shape.
+      expect(_indicator(tester)?.color, color);
+      expect(_indicator(tester)?.shape, shape);
+    });
+  });
 }
 
 Widget _buildWidget(Widget child) {