Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Add indicatorColor & indicatorShape to NavigationRail, `Navigat…
Browse files Browse the repository at this point in the history
…ionDrawer` and move these properties from destination to `NavigationBar` (#117049)
  • Loading branch information
TahaTesser committed Dec 19, 2022
1 parent 32da250 commit cb988c7
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 55 deletions.
45 changes: 34 additions & 11 deletions packages/flutter/lib/src/material/navigation_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ class NavigationBar extends StatelessWidget {
this.elevation,
this.shadowColor,
this.surfaceTintColor,
this.indicatorColor,
this.indicatorShape,
this.height,
this.labelBehavior,
}) : assert(destinations != null && destinations.length >= 2),
Expand Down Expand Up @@ -158,6 +160,20 @@ class NavigationBar extends StatelessWidget {
/// overlay is applied.
final Color? surfaceTintColor;

/// The color of the [indicatorShape] when this destination is selected.
///
/// If null, [NavigationBarThemeData.indicatorColor] is used. If that
/// is also null and [ThemeData.useMaterial3] is true, [ColorScheme.secondaryContainer]
/// is used. Otherwise, [ColorScheme.secondary] with an opacity of 0.24 is used.
final Color? indicatorColor;

/// The shape of the selected inidicator.
///
/// If null, [NavigationBarThemeData.indicatorShape] is used. If that
/// is also null and [ThemeData.useMaterial3] is true, [StadiumBorder] is used.
/// Otherwise, [RoundedRectangleBorder] with a circular border radius of 16 is used.
final ShapeBorder? indicatorShape;

/// The height of the [NavigationBar] itself.
///
/// If this is used in [Scaffold.bottomNavigationBar] and the scaffold is
Expand Down Expand Up @@ -224,6 +240,8 @@ class NavigationBar extends StatelessWidget {
totalNumberOfDestinations: destinations.length,
selectedAnimation: animation,
labelBehavior: effectiveLabelBehavior,
indicatorColor: indicatorColor,
indicatorShape: indicatorShape,
onTap: _handleTap(i),
child: destinations[i],
);
Expand Down Expand Up @@ -274,8 +292,6 @@ class NavigationDestination extends StatelessWidget {
super.key,
required this.icon,
this.selectedIcon,
this.indicatorColor,
this.indicatorShape,
required this.label,
this.tooltip,
});
Expand All @@ -300,12 +316,6 @@ class NavigationDestination extends StatelessWidget {
/// 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].
///
Expand All @@ -324,12 +334,13 @@ class NavigationDestination extends StatelessWidget {

@override
Widget build(BuildContext context) {
final _NavigationDestinationInfo info = _NavigationDestinationInfo.of(context);
const Set<MaterialState> selectedState = <MaterialState>{MaterialState.selected};
const Set<MaterialState> unselectedState = <MaterialState>{};

final NavigationBarThemeData navigationBarTheme = NavigationBarTheme.of(context);
final NavigationBarThemeData defaults = _defaultsFor(context);
final Animation<double> animation = _NavigationDestinationInfo.of(context).selectedAnimation;
final Animation<double> animation = info.selectedAnimation;

return _NavigationDestinationBuilder(
label: label,
Expand All @@ -351,8 +362,8 @@ class NavigationDestination extends StatelessWidget {
children: <Widget>[
NavigationIndicator(
animation: animation,
color: indicatorColor ?? navigationBarTheme.indicatorColor ?? defaults.indicatorColor!,
shape: indicatorShape ?? navigationBarTheme.indicatorShape ?? defaults.indicatorShape!
color: info.indicatorColor ?? navigationBarTheme.indicatorColor ?? defaults.indicatorColor!,
shape: info.indicatorShape ?? navigationBarTheme.indicatorShape ?? defaults.indicatorShape!
),
_StatusTransitionWidgetBuilder(
animation: animation,
Expand Down Expand Up @@ -532,6 +543,8 @@ class _NavigationDestinationInfo extends InheritedWidget {
required this.totalNumberOfDestinations,
required this.selectedAnimation,
required this.labelBehavior,
required this.indicatorColor,
required this.indicatorShape,
required this.onTap,
required super.child,
});
Expand Down Expand Up @@ -588,6 +601,16 @@ class _NavigationDestinationInfo extends InheritedWidget {
/// label, or hide all labels.
final NavigationDestinationLabelBehavior labelBehavior;

/// The color of the selection indicator.
///
/// This is used by destinations to override the indicator color.
final Color? indicatorColor;

/// The shape of the selection indicator.
///
/// This is used by destinations to override the indicator shape.
final ShapeBorder? indicatorShape;

/// The callback that should be called when this destination is tapped.
///
/// This is computed by calling [NavigationBar.onDestinationSelected]
Expand Down
34 changes: 31 additions & 3 deletions packages/flutter/lib/src/material/navigation_drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class NavigationDrawer extends StatelessWidget {
this.shadowColor,
this.surfaceTintColor,
this.elevation,
this.indicatorColor,
this.indicatorShape,
this.onDestinationSelected,
this.selectedIndex = 0,
});
Expand Down Expand Up @@ -90,6 +92,18 @@ class NavigationDrawer extends StatelessWidget {
/// is also null, it will be 1.0.
final double? elevation;

/// The color of the [indicatorShape] when this destination is selected.
///
/// If this is null, [NavigationDrawerThemeData.indicatorColor] is used.
/// If that is also null, defaults to [ColorScheme.secondaryContainer].
final Color? indicatorColor;

/// The shape of the selected inidicator.
///
/// If this is null, [NavigationDrawerThemeData.indicatorShape] is used.
/// If that is also null, defaults to [StadiumBorder].
final ShapeBorder? indicatorShape;

/// Defines the appearance of the items within the navigation drawer.
///
/// The list contains [NavigationDrawerDestination] widgets and/or customized
Expand Down Expand Up @@ -125,6 +139,8 @@ class NavigationDrawer extends StatelessWidget {
index: index,
totalNumberOfDestinations: totalNumberOfDestinations,
selectedAnimation: animation,
indicatorColor: indicatorColor,
indicatorShape: indicatorShape,
onTap: () {
if (onDestinationSelected != null) {
onDestinationSelected!(index);
Expand Down Expand Up @@ -315,9 +331,9 @@ class _NavigationDestinationBuilder extends StatelessWidget {
alignment: Alignment.center,
children: <Widget>[
NavigationIndicator(
animation: _NavigationDrawerDestinationInfo.of(context).selectedAnimation,
color: navigationDrawerTheme.indicatorColor ?? defaults.indicatorColor!,
shape: navigationDrawerTheme.indicatorShape ?? defaults.indicatorShape!,
animation: info.selectedAnimation,
color: info.indicatorColor ?? navigationDrawerTheme.indicatorColor ?? defaults.indicatorColor!,
shape: info.indicatorShape ?? navigationDrawerTheme.indicatorShape ?? defaults.indicatorShape!,
width: (navigationDrawerTheme.indicatorSize ?? defaults.indicatorSize!).width,
height: (navigationDrawerTheme.indicatorSize ?? defaults.indicatorSize!).height,
),
Expand Down Expand Up @@ -433,6 +449,8 @@ class _NavigationDrawerDestinationInfo extends InheritedWidget {
required this.index,
required this.totalNumberOfDestinations,
required this.selectedAnimation,
required this.indicatorColor,
required this.indicatorShape,
required this.onTap,
required super.child,
});
Expand Down Expand Up @@ -478,6 +496,16 @@ class _NavigationDrawerDestinationInfo extends InheritedWidget {
/// to 1 (selected).
final Animation<double> selectedAnimation;

/// The color of the indicator.
///
/// This is used by destinations to override the indicator color.
final Color? indicatorColor;

/// The shape of the indicator.
///
/// This is used by destinations to override the indicator shape.
final ShapeBorder? indicatorShape;

/// The callback that should be called when this destination is tapped.
///
/// This is computed by calling [NavigationDrawer.onDestinationSelected]
Expand Down
21 changes: 20 additions & 1 deletion packages/flutter/lib/src/material/navigation_rail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class NavigationRail extends StatefulWidget {
this.minExtendedWidth,
this.useIndicator,
this.indicatorColor,
this.indicatorShape,
}) : assert(destinations != null && destinations.length >= 2),
assert(selectedIndex == null || (0 <= selectedIndex && selectedIndex < destinations.length)),
assert(elevation == null || elevation > 0),
Expand Down Expand Up @@ -306,8 +307,18 @@ class NavigationRail extends StatefulWidget {

/// Overrides the default value of [NavigationRail]'s selection indicator color,
/// when [useIndicator] is true.
///
/// If this is null, [NavigationRailThemeData.indicatorColor] is used. If
/// that is null, defaults to [ColorScheme.secondaryContainer].
final Color? indicatorColor;

/// Overrides the default value of [NavigationRail]'s selection indicator shape,
/// when [useIndicator] is true.
///
/// If this is null, [NavigationRailThemeData.indicatorShape] is used. If
/// that is null, defaults to [StadiumBorder].
final ShapeBorder? indicatorShape;

/// Returns the animation that controls the [NavigationRail.extended] state.
///
/// This can be used to synchronize animations in the [leading] or [trailing]
Expand Down Expand Up @@ -396,7 +407,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
final NavigationRailLabelType labelType = widget.labelType ?? navigationRailTheme.labelType ?? defaults.labelType!;
final bool useIndicator = widget.useIndicator ?? navigationRailTheme.useIndicator ?? defaults.useIndicator!;
final Color? indicatorColor = widget.indicatorColor ?? navigationRailTheme.indicatorColor ?? defaults.indicatorColor;
final ShapeBorder? indicatorShape = navigationRailTheme.indicatorShape ?? defaults.indicatorShape;
final ShapeBorder? indicatorShape = widget.indicatorShape ?? navigationRailTheme.indicatorShape ?? defaults.indicatorShape;

// For backwards compatibility, in M2 the opacity of the unselected icons needs
// to be set to the default if it isn't in the given theme. This can be removed
Expand Down Expand Up @@ -900,6 +911,8 @@ class NavigationRailDestination {
const NavigationRailDestination({
required this.icon,
Widget? selectedIcon,
this.indicatorColor,
this.indicatorShape,
required this.label,
this.padding,
}) : selectedIcon = selectedIcon ?? icon,
Expand Down Expand Up @@ -933,6 +946,12 @@ class NavigationRailDestination {
/// icons.
final Widget selectedIcon;

/// The color of the [indicatorShape] when this destination is selected.
final Color? indicatorColor;

/// The shape of the selection inidicator.
final ShapeBorder? indicatorShape;

/// The label for the destination.
///
/// The label must be provided when used with the [NavigationRail]. When the
Expand Down
62 changes: 31 additions & 31 deletions packages/flutter/test/material/navigation_bar_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,8 @@ void main() {
expect(_getMaterial(tester).surfaceTintColor, null);
expect(_getMaterial(tester).elevation, 0);
expect(tester.getSize(find.byType(NavigationBar)).height, 80);
expect(_indicator(tester)?.color, const Color(0x3d2196f3));
expect(_indicator(tester)?.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)));
expect(_getIndicatorDecoration(tester)?.color, const Color(0x3d2196f3));
expect(_getIndicatorDecoration(tester)?.shape, RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)));

// M3 settings from the token database.
await tester.pumpWidget(
Expand Down Expand Up @@ -292,8 +292,8 @@ void main() {
expect(_getMaterial(tester).surfaceTintColor, ThemeData().colorScheme.surfaceTint);
expect(_getMaterial(tester).elevation, 3);
expect(tester.getSize(find.byType(NavigationBar)).height, 80);
expect(_indicator(tester)?.color, const Color(0xff2196f3));
expect(_indicator(tester)?.shape, const StadiumBorder());
expect(_getIndicatorDecoration(tester)?.color, const Color(0xff2196f3));
expect(_getIndicatorDecoration(tester)?.shape, const StadiumBorder());
});

testWidgets('NavigationBar shows tooltips with text scaling ', (WidgetTester tester) async {
Expand Down Expand Up @@ -807,21 +807,21 @@ void main() {
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();
const ShapeBorder shape = RoundedRectangleBorder();

Widget buildNaviagationBar({Color? indicatorColor, ShapeBorder? indicatorShape}) {
Widget buildNavigationBar({Color? indicatorColor, ShapeBorder? indicatorShape}) {
return MaterialApp(
theme: theme,
home: Scaffold(
bottomNavigationBar: NavigationBar(
destinations: <Widget>[
indicatorColor: indicatorColor,
indicatorShape: indicatorShape,
destinations: const <Widget>[
NavigationDestination(
icon: const Icon(Icons.ac_unit),
icon: Icon(Icons.ac_unit),
label: 'AC',
indicatorColor: indicatorColor,
indicatorShape: indicatorShape,
),
const NavigationDestination(
NavigationDestination(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
Expand All @@ -832,17 +832,17 @@ void main() {
);
}

await tester.pumpWidget(buildNaviagationBar());
await tester.pumpWidget(buildNavigationBar());

// Test default indicator color and shape.
expect(_indicator(tester)?.color, theme.colorScheme.secondaryContainer);
expect(_indicator(tester)?.shape, const StadiumBorder());
expect(_getIndicatorDecoration(tester)?.color, theme.colorScheme.secondaryContainer);
expect(_getIndicatorDecoration(tester)?.shape, const StadiumBorder());

await tester.pumpWidget(buildNaviagationBar(indicatorColor: color, indicatorShape: shape));
await tester.pumpWidget(buildNavigationBar(indicatorColor: color, indicatorShape: shape));

// Test custom indicator color and shape.
expect(_indicator(tester)?.color, color);
expect(_indicator(tester)?.shape, shape);
expect(_getIndicatorDecoration(tester)?.color, color);
expect(_getIndicatorDecoration(tester)?.shape, shape);
});

group('Material 2', () {
Expand All @@ -852,21 +852,21 @@ void main() {
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();
const ShapeBorder shape = RoundedRectangleBorder();

Widget buildNaviagationBar({Color? indicatorColor, ShapeBorder? indicatorShape}) {
Widget buildNavigationBar({Color? indicatorColor, ShapeBorder? indicatorShape}) {
return MaterialApp(
theme: theme,
home: Scaffold(
bottomNavigationBar: NavigationBar(
destinations: <Widget>[
indicatorColor: indicatorColor,
indicatorShape: indicatorShape,
destinations: const <Widget>[
NavigationDestination(
icon: const Icon(Icons.ac_unit),
icon: Icon(Icons.ac_unit),
label: 'AC',
indicatorColor: indicatorColor,
indicatorShape: indicatorShape,
),
const NavigationDestination(
NavigationDestination(
icon: Icon(Icons.access_alarm),
label: 'Alarm',
),
Expand All @@ -877,20 +877,20 @@ void main() {
);
}

await tester.pumpWidget(buildNaviagationBar());
await tester.pumpWidget(buildNavigationBar());

// Test default indicator color and shape.
expect(_indicator(tester)?.color, theme.colorScheme.secondary.withOpacity(0.24));
expect(_getIndicatorDecoration(tester)?.color, theme.colorScheme.secondary.withOpacity(0.24));
expect(
_indicator(tester)?.shape,
_getIndicatorDecoration(tester)?.shape,
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))),
);

await tester.pumpWidget(buildNaviagationBar(indicatorColor: color, indicatorShape: shape));
await tester.pumpWidget(buildNavigationBar(indicatorColor: color, indicatorShape: shape));

// Test custom indicator color and shape.
expect(_indicator(tester)?.color, color);
expect(_indicator(tester)?.shape, shape);
expect(_getIndicatorDecoration(tester)?.color, color);
expect(_getIndicatorDecoration(tester)?.shape, shape);
});
});
}
Expand All @@ -912,7 +912,7 @@ Material _getMaterial(WidgetTester tester) {
);
}

ShapeDecoration? _indicator(WidgetTester tester) {
ShapeDecoration? _getIndicatorDecoration(WidgetTester tester) {
return tester.firstWidget<Container>(
find.descendant(
of: find.byType(FadeTransition),
Expand Down
Loading

0 comments on commit cb988c7

Please sign in to comment.