diff --git a/packages/flutter_adaptive_scaffold/CHANGELOG.md b/packages/flutter_adaptive_scaffold/CHANGELOG.md index 921b478344f3..859135008194 100644 --- a/packages/flutter_adaptive_scaffold/CHANGELOG.md +++ b/packages/flutter_adaptive_scaffold/CHANGELOG.md @@ -1,7 +1,11 @@ -## NEXT +## 0.1.12 * Updates minimum supported SDK version to Flutter 3.22/Dart 3.4. * Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. +* Expose `labelType` for NavigationRails. +* Add `navigationRailDestinationBuilder` to apply custom Destinations. +* Add `groupAlignment` property to change alignment. +* Set `navRailTheme` when using the Drawer just like the other NavigationRails. ## 0.1.11+1 diff --git a/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart b/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart index c2a2e68327bf..638dcf992349 100644 --- a/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart +++ b/packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart @@ -24,6 +24,13 @@ const double kMaterialMediumMinMargin = 12; /// design 3 spec. const double kMaterialExpandedMinMargin = 32; +/// Signature for a builder used by [AdaptiveScaffold.navigationRailDestinationBuilder] that converts a +/// [NavigationDestination] to a [NavigationRailDestination]. +typedef NavigationRailDestinationBuilder = NavigationRailDestination Function( + int index, + NavigationDestination destination, +); + /// Implements the basic visual layout structure for /// [Material Design 3](https://m3.material.io/foundations/adaptive-design/overview) /// that adapts to a variety of screens. @@ -103,6 +110,8 @@ class AdaptiveScaffold extends StatefulWidget { this.navigationRailWidth = 72, this.extendedNavigationRailWidth = 192, this.appBarBreakpoint, + this.navigationRailDestinationBuilder, + this.groupAlignment, }) : assert( destinations.length >= 2, 'At least two destinations are required', @@ -129,6 +138,9 @@ class AdaptiveScaffold extends StatefulWidget { /// navigation rail at the largest breakpoint. final Widget? trailingNavRail; + /// The alignment of the destinations in the navigation rail. + final double? groupAlignment; + /// Widget to be displayed in the body slot at the smallest breakpoint. /// /// If nothing is entered for this property, then the default [body] is @@ -246,6 +258,9 @@ class AdaptiveScaffold extends StatefulWidget { /// [Breakpoint]. final double extendedNavigationRailWidth; + /// Used to map NavigationDestination to NavigationRailDestination. + final NavigationRailDestinationBuilder? navigationRailDestinationBuilder; + /// Callback function for when the index of a [NavigationRail] changes. static WidgetBuilder emptyBuilder = (_) => const SizedBox(); @@ -267,6 +282,9 @@ class AdaptiveScaffold extends StatefulWidget { /// Takes in a [selectedIndex] property for the current selected item in /// the [NavigationRail] and [extended] for whether the [NavigationRail] /// is extended or not. + /// + /// If [labelType] is null, then the default value is + /// [NavigationRailLabelType.none]. static Builder standardNavigationRail({ required List destinations, double width = 72, @@ -282,7 +300,7 @@ class AdaptiveScaffold extends StatefulWidget { IconThemeData? unselectedIconTheme, TextStyle? selectedLabelTextStyle, TextStyle? unSelectedLabelTextStyle, - NavigationRailLabelType labelType = NavigationRailLabelType.none, + NavigationRailLabelType? labelType = NavigationRailLabelType.none, }) { if (extended && width == 72) { width = 192; @@ -513,6 +531,13 @@ class _AdaptiveScaffoldState extends State { final NavigationRailThemeData navRailTheme = Theme.of(context).navigationRailTheme; + final List destinations = widget.destinations + .map((NavigationDestination destination) => + widget.navigationRailDestinationBuilder + ?.call(widget.destinations.indexOf(destination), destination) ?? + AdaptiveScaffold.toRailDestination(destination)) + .toList(); + return Scaffold( key: _scaffoldKey, appBar: widget.drawerBreakpoint.isActive(context) && widget.useDrawer || @@ -526,11 +551,15 @@ class _AdaptiveScaffoldState extends State { leading: widget.leadingExtendedNavRail, trailing: widget.trailingNavRail, selectedIndex: widget.selectedIndex, - destinations: widget.destinations - .map((NavigationDestination destination) => - AdaptiveScaffold.toRailDestination(destination)) - .toList(), + destinations: destinations, onDestinationSelected: _onDrawerDestinationSelected, + backgroundColor: navRailTheme.backgroundColor, + selectedIconTheme: navRailTheme.selectedIconTheme, + unselectedIconTheme: navRailTheme.unselectedIconTheme, + selectedLabelTextStyle: navRailTheme.selectedLabelTextStyle, + unselectedLabelTextStyle: navRailTheme.unselectedLabelTextStyle, + groupAlignment: widget.groupAlignment, + labelType: navRailTheme.labelType, ), ) : null, @@ -548,16 +577,15 @@ class _AdaptiveScaffoldState extends State { leading: widget.leadingUnextendedNavRail, trailing: widget.trailingNavRail, selectedIndex: widget.selectedIndex, - destinations: widget.destinations - .map((NavigationDestination destination) => - AdaptiveScaffold.toRailDestination(destination)) - .toList(), + destinations: destinations, onDestinationSelected: widget.onSelectedIndexChange, backgroundColor: navRailTheme.backgroundColor, selectedIconTheme: navRailTheme.selectedIconTheme, unselectedIconTheme: navRailTheme.unselectedIconTheme, selectedLabelTextStyle: navRailTheme.selectedLabelTextStyle, unSelectedLabelTextStyle: navRailTheme.unselectedLabelTextStyle, + labelType: navRailTheme.labelType, + groupAlignment: widget.groupAlignment, ), ), widget.largeBreakpoint: SlotLayout.from( @@ -568,16 +596,15 @@ class _AdaptiveScaffoldState extends State { leading: widget.leadingExtendedNavRail, trailing: widget.trailingNavRail, selectedIndex: widget.selectedIndex, - destinations: widget.destinations - .map((NavigationDestination destination) => - AdaptiveScaffold.toRailDestination(destination)) - .toList(), + destinations: destinations, onDestinationSelected: widget.onSelectedIndexChange, backgroundColor: navRailTheme.backgroundColor, selectedIconTheme: navRailTheme.selectedIconTheme, unselectedIconTheme: navRailTheme.unselectedIconTheme, selectedLabelTextStyle: navRailTheme.selectedLabelTextStyle, unSelectedLabelTextStyle: navRailTheme.unselectedLabelTextStyle, + labelType: navRailTheme.labelType, + groupAlignment: widget.groupAlignment, ), ), }, diff --git a/packages/flutter_adaptive_scaffold/pubspec.yaml b/packages/flutter_adaptive_scaffold/pubspec.yaml index 5ac956ffcf83..93f7d409e1a4 100644 --- a/packages/flutter_adaptive_scaffold/pubspec.yaml +++ b/packages/flutter_adaptive_scaffold/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_adaptive_scaffold description: Widgets to easily build adaptive layouts, including navigation elements. -version: 0.1.11+1 +version: 0.1.12 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_adaptive_scaffold%22 repository: https://github.com/flutter/packages/tree/main/packages/flutter_adaptive_scaffold @@ -19,3 +19,4 @@ dev_dependencies: topics: - layout - ui + - adaptive diff --git a/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart b/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart index 8dcaf2d5a0a4..40b6754d30b0 100644 --- a/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart +++ b/packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart @@ -618,45 +618,45 @@ void main() { // creates a NavigationRail widget as expected with groupAlignment provided, // and checks whether the NavigationRail's groupAlignment matches the expected value. testWidgets( - 'groupAligment parameter of AdaptiveScaffold.standardNavigationRail works correctly', - (WidgetTester tester) async { - const List destinations = - [ - NavigationRailDestination( - icon: Icon(Icons.home), - label: Text('Home'), - ), - NavigationRailDestination( - icon: Icon(Icons.account_circle), - label: Text('Profile'), - ), - NavigationRailDestination( - icon: Icon(Icons.settings), - label: Text('Settings'), - ), - ]; + 'groupAligment parameter of AdaptiveScaffold.standardNavigationRail works correctly', + (WidgetTester tester) async { + const List destinations = + [ + NavigationRailDestination( + icon: Icon(Icons.home), + label: Text('Home'), + ), + NavigationRailDestination( + icon: Icon(Icons.account_circle), + label: Text('Profile'), + ), + NavigationRailDestination( + icon: Icon(Icons.settings), + label: Text('Settings'), + ), + ]; - // Align to bottom. - const double groupAlignment = 1.0; + const double groupAlignment = 1.0; - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: Builder( - builder: (BuildContext context) { - return AdaptiveScaffold.standardNavigationRail( - destinations: destinations, - groupAlignment: groupAlignment, - ); - }, + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Builder( + builder: (BuildContext context) { + return AdaptiveScaffold.standardNavigationRail( + destinations: destinations, + groupAlignment: groupAlignment, + ); + }, + ), ), ), - ), - ); - final NavigationRail rail = - tester.widget(find.byType(NavigationRail)); - expect(rail.groupAlignment, equals(groupAlignment)); - }); + ); + final NavigationRail rail = + tester.widget(find.byType(NavigationRail)); + expect(rail.groupAlignment, equals(groupAlignment)); + }, + ); testWidgets( "doesn't override Directionality", @@ -744,6 +744,81 @@ void main() { ); }, ); + + // Test for navigationRailDestinationBuilder parameter. + testWidgets('adaptive scaffold custom navigation rail destination mapping', + (WidgetTester tester) async { + const List destinations = [ + NavigationDestination( + icon: Icon(Icons.home), + label: 'Home', + ), + NavigationDestination( + icon: Icon(Icons.account_circle), + label: 'Profile', + ), + ]; + + NavigationRailDestination customMapping( + int index, NavigationDestination destination) { + return NavigationRailDestination( + icon: destination.icon, + label: Text('Custom ${destination.label}'), + ); + } + + await tester.pumpWidget( + MaterialApp( + home: MediaQuery( + data: const MediaQueryData(size: Size(800, 600)), + child: AdaptiveScaffold( + destinations: destinations, + navigationRailDestinationBuilder: customMapping, + ), + ), + ), + ); + + expect(find.text('Custom Home'), findsOneWidget); + expect(find.text('Custom Profile'), findsOneWidget); + }); + + // Test for labelType setting through the navigation rail theme. + testWidgets( + 'adaptive scaffold respects NavigationRailLabelType from theme', + (WidgetTester tester) async { + const List destinations = [ + NavigationDestination( + icon: Icon(Icons.home), + label: 'Home', + ), + NavigationDestination( + icon: Icon(Icons.account_circle), + label: 'Profile', + ), + ]; + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData( + navigationRailTheme: const NavigationRailThemeData( + labelType: NavigationRailLabelType.all, + ), + ), + home: MediaQuery( + data: const MediaQueryData(size: Size(800, 600)), + child: AdaptiveScaffold( + destinations: destinations, + ), + ), + ), + ); + + final NavigationRail rail = + tester.widget(find.byType(NavigationRail)); + expect(rail.labelType, NavigationRailLabelType.all); + }, + ); } /// An empty widget that implements [PreferredSizeWidget] to ensure that