Skip to content

[flutter_adaptive_scaffold] Map NavigationsRails destinations and use group and labelType #7310

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/flutter_adaptive_scaffold/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
53 changes: 40 additions & 13 deletions packages/flutter_adaptive_scaffold/lib/src/adaptive_scaffold.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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',
Expand All @@ -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
Expand Down Expand Up @@ -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();

Expand All @@ -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<NavigationRailDestination> destinations,
double width = 72,
Expand All @@ -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;
Expand Down Expand Up @@ -513,6 +531,13 @@ class _AdaptiveScaffoldState extends State<AdaptiveScaffold> {
final NavigationRailThemeData navRailTheme =
Theme.of(context).navigationRailTheme;

final List<NavigationRailDestination> 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 ||
Expand All @@ -526,11 +551,15 @@ class _AdaptiveScaffoldState extends State<AdaptiveScaffold> {
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,
Expand All @@ -548,16 +577,15 @@ class _AdaptiveScaffoldState extends State<AdaptiveScaffold> {
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(
Expand All @@ -568,16 +596,15 @@ class _AdaptiveScaffoldState extends State<AdaptiveScaffold> {
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,
),
),
},
Expand Down
3 changes: 2 additions & 1 deletion packages/flutter_adaptive_scaffold/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -19,3 +19,4 @@ dev_dependencies:
topics:
- layout
- ui
- adaptive
145 changes: 110 additions & 35 deletions packages/flutter_adaptive_scaffold/test/adaptive_scaffold_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<NavigationRailDestination> destinations =
<NavigationRailDestination>[
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<NavigationRailDestination> destinations =
<NavigationRailDestination>[
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<NavigationRail>(find.byType(NavigationRail));
expect(rail.groupAlignment, equals(groupAlignment));
});
);
final NavigationRail rail =
tester.widget<NavigationRail>(find.byType(NavigationRail));
expect(rail.groupAlignment, equals(groupAlignment));
},
);

testWidgets(
"doesn't override Directionality",
Expand Down Expand Up @@ -744,6 +744,81 @@ void main() {
);
},
);

// Test for navigationRailDestinationBuilder parameter.
testWidgets('adaptive scaffold custom navigation rail destination mapping',
(WidgetTester tester) async {
const List<NavigationDestination> destinations = <NavigationDestination>[
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<NavigationDestination> destinations = <NavigationDestination>[
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<NavigationRail>(find.byType(NavigationRail));
expect(rail.labelType, NavigationRailLabelType.all);
},
);
}

/// An empty widget that implements [PreferredSizeWidget] to ensure that
Expand Down