diff --git a/examples/api/lib/material/navigation_rail/navigation_rail.0.dart b/examples/api/lib/material/navigation_rail/navigation_rail.0.dart index adf78c617bb62..b0eeedb1c79fd 100644 --- a/examples/api/lib/material/navigation_rail/navigation_rail.0.dart +++ b/examples/api/lib/material/navigation_rail/navigation_rail.0.dart @@ -34,151 +34,127 @@ class _NavRailExampleState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: Row( - children: [ - NavigationRail( - selectedIndex: _selectedIndex, - groupAlignment: groupAlignment, - onDestinationSelected: (int index) { - setState(() { - _selectedIndex = index; - }); - }, - labelType: labelType, - leading: - showLeading - ? FloatingActionButton( - elevation: 0, - onPressed: () { - // Add your onPressed code here! - }, - child: const Icon(Icons.add), - ) - : const SizedBox(), - trailing: - showTrailing - ? IconButton( - onPressed: () { - // Add your onPressed code here! - }, - icon: const Icon(Icons.more_horiz_rounded), - ) - : const SizedBox(), - destinations: const [ - NavigationRailDestination( - icon: Icon(Icons.favorite_border), - selectedIcon: Icon(Icons.favorite), - label: Text('First'), - ), - NavigationRailDestination( - icon: Icon(Icons.bookmark_border), - selectedIcon: Icon(Icons.book), - label: Text('Second'), - ), - NavigationRailDestination( - icon: Icon(Icons.star_border), - selectedIcon: Icon(Icons.star), - label: Text('Third'), - ), - ], - ), - const VerticalDivider(thickness: 1, width: 1), - // This is the main content. - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('selectedIndex: $_selectedIndex'), - const SizedBox(height: 20), - Text('Label type: ${labelType.name}'), - const SizedBox(height: 10), - OverflowBar( - spacing: 10.0, - children: [ - ElevatedButton( - onPressed: () { - setState(() { - labelType = NavigationRailLabelType.none; - }); - }, - child: const Text('None'), - ), - ElevatedButton( - onPressed: () { - setState(() { - labelType = NavigationRailLabelType.selected; - }); - }, - child: const Text('Selected'), - ), - ElevatedButton( - onPressed: () { - setState(() { - labelType = NavigationRailLabelType.all; - }); - }, - child: const Text('All'), - ), - ], + body: SafeArea( + child: Row( + children: [ + NavigationRail( + selectedIndex: _selectedIndex, + groupAlignment: groupAlignment, + onDestinationSelected: (int index) { + setState(() { + _selectedIndex = index; + }); + }, + labelType: labelType, + leading: + showLeading + ? FloatingActionButton( + elevation: 0, + onPressed: () { + // Add your onPressed code here! + }, + child: const Icon(Icons.add), + ) + : const SizedBox(), + trailing: + showTrailing + ? IconButton( + onPressed: () { + // Add your onPressed code here! + }, + icon: const Icon(Icons.more_horiz_rounded), + ) + : const SizedBox(), + destinations: const [ + NavigationRailDestination( + icon: Icon(Icons.favorite_border), + selectedIcon: Icon(Icons.favorite), + label: Text('First'), ), - const SizedBox(height: 20), - Text('Group alignment: $groupAlignment'), - const SizedBox(height: 10), - OverflowBar( - spacing: 10.0, - children: [ - ElevatedButton( - onPressed: () { - setState(() { - groupAlignment = -1.0; - }); - }, - child: const Text('Top'), - ), - ElevatedButton( - onPressed: () { - setState(() { - groupAlignment = 0.0; - }); - }, - child: const Text('Center'), - ), - ElevatedButton( - onPressed: () { - setState(() { - groupAlignment = 1.0; - }); - }, - child: const Text('Bottom'), - ), - ], + NavigationRailDestination( + icon: Badge(child: Icon(Icons.bookmark_border)), + selectedIcon: Badge(child: Icon(Icons.book)), + label: Text('Second'), ), - const SizedBox(height: 20), - OverflowBar( - spacing: 10.0, - children: [ - ElevatedButton( - onPressed: () { - setState(() { - showLeading = !showLeading; - }); - }, - child: Text(showLeading ? 'Hide Leading' : 'Show Leading'), - ), - ElevatedButton( - onPressed: () { - setState(() { - showTrailing = !showTrailing; - }); - }, - child: Text(showTrailing ? 'Hide Trailing' : 'Show Trailing'), - ), - ], + NavigationRailDestination( + icon: Badge(label: Text('4'), child: Icon(Icons.star_border)), + selectedIcon: Badge(label: Text('4'), child: Icon(Icons.star)), + label: Text('Third'), ), ], ), - ), - ], + const VerticalDivider(thickness: 1, width: 1), + // This is the main content. + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('selectedIndex: $_selectedIndex'), + const SizedBox(height: 20), + Text('Label type: ${labelType.name}'), + const SizedBox(height: 10), + SegmentedButton( + segments: const >[ + ButtonSegment( + value: NavigationRailLabelType.none, + label: Text('None'), + ), + ButtonSegment( + value: NavigationRailLabelType.selected, + label: Text('Selected'), + ), + ButtonSegment( + value: NavigationRailLabelType.all, + label: Text('All'), + ), + ], + selected: {labelType}, + onSelectionChanged: (Set newSelection) { + setState(() { + labelType = newSelection.first; + }); + }, + ), + const SizedBox(height: 20), + Text('Group alignment: $groupAlignment'), + const SizedBox(height: 10), + SegmentedButton( + segments: const >[ + ButtonSegment(value: -1.0, label: Text('Top')), + ButtonSegment(value: 0.0, label: Text('Center')), + ButtonSegment(value: 1.0, label: Text('Bottom')), + ], + selected: {groupAlignment}, + onSelectionChanged: (Set newSelection) { + setState(() { + groupAlignment = newSelection.first; + }); + }, + ), + const SizedBox(height: 20), + SwitchListTile( + title: Text(showLeading ? 'Hide Leading' : 'Show Leading'), + value: showLeading, + onChanged: (bool value) { + setState(() { + showLeading = value; + }); + }, + ), + SwitchListTile( + title: Text(showTrailing ? 'Hide Trailing' : 'Show Trailing'), + value: showTrailing, + onChanged: (bool value) { + setState(() { + showTrailing = value; + }); + }, + ), + ], + ), + ), + ], + ), ), ); } diff --git a/examples/api/lib/material/navigation_rail/navigation_rail.1.dart b/examples/api/lib/material/navigation_rail/navigation_rail.1.dart deleted file mode 100644 index 4edc30919ef7a..0000000000000 --- a/examples/api/lib/material/navigation_rail/navigation_rail.1.dart +++ /dev/null @@ -1,187 +0,0 @@ -// 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'; - -/// Flutter code sample for [NavigationRail]. - -void main() => runApp(const NavigationRailExampleApp()); - -class NavigationRailExampleApp extends StatelessWidget { - const NavigationRailExampleApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp(theme: ThemeData(useMaterial3: true), home: const NavRailExample()); - } -} - -class NavRailExample extends StatefulWidget { - const NavRailExample({super.key}); - - @override - State createState() => _NavRailExampleState(); -} - -class _NavRailExampleState extends State { - int _selectedIndex = 0; - NavigationRailLabelType labelType = NavigationRailLabelType.all; - bool showLeading = false; - bool showTrailing = false; - double groupAlignment = -1.0; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: SafeArea( - child: Row( - children: [ - NavigationRail( - selectedIndex: _selectedIndex, - groupAlignment: groupAlignment, - onDestinationSelected: (int index) { - setState(() { - _selectedIndex = index; - }); - }, - labelType: labelType, - leading: - showLeading - ? FloatingActionButton( - elevation: 0, - onPressed: () { - // Add your onPressed code here! - }, - child: const Icon(Icons.add), - ) - : const SizedBox(), - trailing: - showTrailing - ? IconButton( - onPressed: () { - // Add your onPressed code here! - }, - icon: const Icon(Icons.more_horiz_rounded), - ) - : const SizedBox(), - destinations: const [ - NavigationRailDestination( - icon: Icon(Icons.favorite_border), - selectedIcon: Icon(Icons.favorite), - label: Text('First'), - ), - NavigationRailDestination( - icon: Badge(child: Icon(Icons.bookmark_border)), - selectedIcon: Badge(child: Icon(Icons.book)), - label: Text('Second'), - ), - NavigationRailDestination( - icon: Badge(label: Text('4'), child: Icon(Icons.star_border)), - selectedIcon: Badge(label: Text('4'), child: Icon(Icons.star)), - label: Text('Third'), - ), - ], - ), - const VerticalDivider(thickness: 1, width: 1), - // This is the main content. - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('selectedIndex: $_selectedIndex'), - const SizedBox(height: 20), - Text('Label type: ${labelType.name}'), - const SizedBox(height: 10), - OverflowBar( - spacing: 10.0, - children: [ - ElevatedButton( - onPressed: () { - setState(() { - labelType = NavigationRailLabelType.none; - }); - }, - child: const Text('None'), - ), - ElevatedButton( - onPressed: () { - setState(() { - labelType = NavigationRailLabelType.selected; - }); - }, - child: const Text('Selected'), - ), - ElevatedButton( - onPressed: () { - setState(() { - labelType = NavigationRailLabelType.all; - }); - }, - child: const Text('All'), - ), - ], - ), - const SizedBox(height: 20), - Text('Group alignment: $groupAlignment'), - const SizedBox(height: 10), - OverflowBar( - spacing: 10.0, - children: [ - ElevatedButton( - onPressed: () { - setState(() { - groupAlignment = -1.0; - }); - }, - child: const Text('Top'), - ), - ElevatedButton( - onPressed: () { - setState(() { - groupAlignment = 0.0; - }); - }, - child: const Text('Center'), - ), - ElevatedButton( - onPressed: () { - setState(() { - groupAlignment = 1.0; - }); - }, - child: const Text('Bottom'), - ), - ], - ), - const SizedBox(height: 20), - OverflowBar( - spacing: 10.0, - children: [ - ElevatedButton( - onPressed: () { - setState(() { - showLeading = !showLeading; - }); - }, - child: Text(showLeading ? 'Hide Leading' : 'Show Leading'), - ), - ElevatedButton( - onPressed: () { - setState(() { - showTrailing = !showTrailing; - }); - }, - child: Text(showTrailing ? 'Hide Trailing' : 'Show Trailing'), - ), - ], - ), - ], - ), - ), - ], - ), - ), - ); - } -} diff --git a/examples/api/test/material/navigation_rail/navigation_rail.0_test.dart b/examples/api/test/material/navigation_rail/navigation_rail.0_test.dart index 93618606293c8..8da63b28c1a8a 100644 --- a/examples/api/test/material/navigation_rail/navigation_rail.0_test.dart +++ b/examples/api/test/material/navigation_rail/navigation_rail.0_test.dart @@ -7,7 +7,7 @@ import 'package:flutter_api_samples/material/navigation_rail/navigation_rail.0.d import 'package:flutter_test/flutter_test.dart'; void main() { - testWidgets('Navigation rail updates destination on tap', (WidgetTester tester) async { + testWidgets('NavigationRail updates destination on tap', (WidgetTester tester) async { await tester.pumpWidget(const example.NavigationRailExampleApp()); final NavigationRail navigationRailWidget = tester.firstWidget(find.byType(NavigationRail)); @@ -30,19 +30,19 @@ void main() { expect(find.text('selectedIndex: 2'), findsOneWidget); }); - testWidgets('Navigation rail updates label type', (WidgetTester tester) async { + testWidgets('NavigationRail updates label type', (WidgetTester tester) async { await tester.pumpWidget(const example.NavigationRailExampleApp()); // initial label type set to all. expect(find.text('Label type: all'), findsOneWidget); // switch to selected label type - await tester.tap(find.widgetWithText(ElevatedButton, 'Selected')); + await tester.tap(find.text('Selected')); await tester.pumpAndSettle(); expect(find.text('Label type: selected'), findsOneWidget); // switch to none label type - await tester.tap(find.widgetWithText(ElevatedButton, 'None')); + await tester.tap(find.text('None')); await tester.pumpAndSettle(); expect(find.text('Label type: none'), findsOneWidget); }); @@ -54,17 +54,17 @@ void main() { expect(find.text('Group alignment: -1.0'), findsOneWidget); // switch to center alignment - await tester.tap(find.widgetWithText(ElevatedButton, 'Center')); + await tester.tap(find.text('Center')); await tester.pumpAndSettle(); expect(find.text('Group alignment: 0.0'), findsOneWidget); // switch to bottom alignment - await tester.tap(find.widgetWithText(ElevatedButton, 'Bottom')); + await tester.tap(find.text('Bottom')); await tester.pumpAndSettle(); expect(find.text('Group alignment: 1.0'), findsOneWidget); }); - testWidgets('Navigation rail shows leading/trailing widgets', (WidgetTester tester) async { + testWidgets('NavigationRail shows leading/trailing widgets', (WidgetTester tester) async { await tester.pumpWidget(const example.NavigationRailExampleApp()); // Initially leading/trailing widgets are hidden. @@ -72,15 +72,31 @@ void main() { expect(find.byType(IconButton), findsNothing); // Tap to show leading Widget. - await tester.tap(find.widgetWithText(ElevatedButton, 'Show Leading')); + await tester.tap(find.text('Show Leading')); await tester.pumpAndSettle(); expect(find.byType(FloatingActionButton), findsOneWidget); expect(find.byType(IconButton), findsNothing); // Tap to show trailing Widget. - await tester.tap(find.widgetWithText(ElevatedButton, 'Show Trailing')); + await tester.tap(find.text('Show Trailing')); await tester.pumpAndSettle(); expect(find.byType(FloatingActionButton), findsOneWidget); expect(find.byType(IconButton), findsOneWidget); }); + + testWidgets('Destinations have badge', (WidgetTester tester) async { + await tester.pumpWidget(const example.NavigationRailExampleApp()); + + // Test badge without label. + final Badge notificationBadge = tester.firstWidget( + find.ancestor(of: find.byIcon(Icons.bookmark_border), matching: find.byType(Badge)), + ); + expect(notificationBadge.label, null); + + // Test badge with label. + final Badge messagesBadge = tester.firstWidget( + find.ancestor(of: find.byIcon(Icons.star_border), matching: find.byType(Badge)), + ); + expect(messagesBadge.label, isNotNull); + }); } diff --git a/examples/api/test/material/navigation_rail/navigation_rail.1_test.dart b/examples/api/test/material/navigation_rail/navigation_rail.1_test.dart deleted file mode 100644 index 0b36bd8607507..0000000000000 --- a/examples/api/test/material/navigation_rail/navigation_rail.1_test.dart +++ /dev/null @@ -1,102 +0,0 @@ -// 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_rail/navigation_rail.1.dart' as example; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - testWidgets('Navigation rail updates destination on tap', (WidgetTester tester) async { - await tester.pumpWidget(const example.NavigationRailExampleApp()); - final NavigationRail navigationRailWidget = tester.firstWidget(find.byType(NavigationRail)); - - /// NavigationRailDestinations must be rendered - expect(find.text('First'), findsOneWidget); - expect(find.text('Second'), findsOneWidget); - expect(find.text('Third'), findsOneWidget); - - /// initial index must be zero - expect(navigationRailWidget.selectedIndex, 0); - - /// switch to second tab - await tester.tap(find.text('Second')); - await tester.pumpAndSettle(); - expect(find.text('selectedIndex: 1'), findsOneWidget); - - /// switch to third tab - await tester.tap(find.text('Third')); - await tester.pumpAndSettle(); - expect(find.text('selectedIndex: 2'), findsOneWidget); - }); - - testWidgets('Navigation rail updates label type', (WidgetTester tester) async { - await tester.pumpWidget(const example.NavigationRailExampleApp()); - - // initial label type set to all. - expect(find.text('Label type: all'), findsOneWidget); - - // switch to selected label type - await tester.tap(find.widgetWithText(ElevatedButton, 'Selected')); - await tester.pumpAndSettle(); - expect(find.text('Label type: selected'), findsOneWidget); - - // switch to none label type - await tester.tap(find.widgetWithText(ElevatedButton, 'None')); - await tester.pumpAndSettle(); - expect(find.text('Label type: none'), findsOneWidget); - }); - - testWidgets('Navigation rail updates group alignment', (WidgetTester tester) async { - await tester.pumpWidget(const example.NavigationRailExampleApp()); - - // initial group alignment set top top. - expect(find.text('Group alignment: -1.0'), findsOneWidget); - - // switch to center alignment - await tester.tap(find.widgetWithText(ElevatedButton, 'Center')); - await tester.pumpAndSettle(); - expect(find.text('Group alignment: 0.0'), findsOneWidget); - - // switch to bottom alignment - await tester.tap(find.widgetWithText(ElevatedButton, 'Bottom')); - await tester.pumpAndSettle(); - expect(find.text('Group alignment: 1.0'), findsOneWidget); - }); - - testWidgets('Navigation rail shows leading/trailing widgets', (WidgetTester tester) async { - await tester.pumpWidget(const example.NavigationRailExampleApp()); - - // Initially leading/trailing widgets are hidden. - expect(find.byType(FloatingActionButton), findsNothing); - expect(find.byType(IconButton), findsNothing); - - // Tap to show leading Widget. - await tester.tap(find.widgetWithText(ElevatedButton, 'Show Leading')); - await tester.pumpAndSettle(); - expect(find.byType(FloatingActionButton), findsOneWidget); - expect(find.byType(IconButton), findsNothing); - - // Tap to show trailing Widget. - await tester.tap(find.widgetWithText(ElevatedButton, 'Show Trailing')); - await tester.pumpAndSettle(); - expect(find.byType(FloatingActionButton), findsOneWidget); - expect(find.byType(IconButton), findsOneWidget); - }); - - testWidgets('Destinations have badge', (WidgetTester tester) async { - await tester.pumpWidget(const example.NavigationRailExampleApp()); - - // Test badge without label. - final Badge notificationBadge = tester.firstWidget( - find.ancestor(of: find.byIcon(Icons.bookmark_border), matching: find.byType(Badge)), - ); - expect(notificationBadge.label, null); - - // Test badge with label. - final Badge messagesBadge = tester.firstWidget( - find.ancestor(of: find.byIcon(Icons.star_border), matching: find.byType(Badge)), - ); - expect(messagesBadge.label, isNotNull); - }); -} diff --git a/packages/flutter/lib/src/material/navigation_rail.dart b/packages/flutter/lib/src/material/navigation_rail.dart index 1d8d7e3f8584e..2ec7a4ea7cdbf 100644 --- a/packages/flutter/lib/src/material/navigation_rail.dart +++ b/packages/flutter/lib/src/material/navigation_rail.dart @@ -49,19 +49,10 @@ const double _kIndicatorHeight = 32; /// for an example. /// /// {@tool dartpad} -/// This example shows a [NavigationRail] used within a Scaffold with 3 -/// [NavigationRailDestination]s. The main content is separated by a divider -/// (although elevation on the navigation rail can be used instead). The -/// `_selectedIndex` is updated by the `onDestinationSelected` callback. -/// -/// ** See code in examples/api/lib/material/navigation_rail/navigation_rail.0.dart ** -/// {@end-tool} -/// -/// {@tool dartpad} /// This sample shows the creation of [NavigationRail] widget used within a Scaffold with 3 /// [NavigationRailDestination]s, as described in: https://m3.material.io/components/navigation-rail/overview /// -/// ** See code in examples/api/lib/material/navigation_rail/navigation_rail.1.dart ** +/// ** See code in examples/api/lib/material/navigation_rail/navigation_rail.0.dart ** /// {@end-tool} /// /// See also: