Skip to content

Commit

Permalink
Add IndicatorShape to NavigationRailTheme and fix indicator rippl…
Browse files Browse the repository at this point in the history
…e. (#116108)

* Add `IndicatorShape` to `NavigationRailTheme` and fix  indicator ripple.

* remove unused variables
  • Loading branch information
TahaTesser authored Nov 28, 2022
1 parent 215f637 commit beaabb7
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 47 deletions.
17 changes: 9 additions & 8 deletions dev/tools/gen_defaults/lib/navigation_rail_template.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ class NavigationRailTemplate extends TokenTemplate {
String generate() => '''
class _${blockName}DefaultsM3 extends NavigationRailThemeData {
_${blockName}DefaultsM3(this.context)
: super(
elevation: ${elevation("md.comp.navigation-rail.container")},
groupAlignment: -1,
labelType: NavigationRailLabelType.none,
useIndicator: true,
minWidth: ${tokens["md.comp.navigation-rail.container.width"]},
minExtendedWidth: 256,
);
: super(
elevation: ${elevation("md.comp.navigation-rail.container")},
groupAlignment: -1,
labelType: NavigationRailLabelType.none,
useIndicator: true,
minWidth: ${tokens["md.comp.navigation-rail.container.width"]},
minExtendedWidth: 256,
);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
Expand Down Expand Up @@ -53,6 +53,7 @@ class _${blockName}DefaultsM3 extends NavigationRailThemeData {
@override Color? get indicatorColor => ${componentColor("md.comp.navigation-rail.active-indicator")};
@override ShapeBorder? get indicatorShape => ${shape("md.comp.navigation-rail.active-indicator")};
}
''';
}
112 changes: 82 additions & 30 deletions packages/flutter/lib/src/material/navigation_rail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import 'navigation_rail_theme.dart';
import 'text_theme.dart';
import 'theme.dart';

const double _kCircularIndicatorDiameter = 56;

/// A Material Design widget that is meant to be displayed at the left or right of an
/// app to navigate between a small number of views, typically between three and
/// five.
Expand Down Expand Up @@ -394,6 +396,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;

// 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 @@ -443,6 +446,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
padding: widget.destinations[i].padding,
useIndicator: useIndicator,
indicatorColor: useIndicator ? indicatorColor : null,
indicatorShape: useIndicator ? indicatorShape : null,
onTap: () {
if (widget.onDestinationSelected != null) {
widget.onDestinationSelected!(i);
Expand Down Expand Up @@ -529,6 +533,7 @@ class _RailDestination extends StatelessWidget {
this.padding,
required this.useIndicator,
this.indicatorColor,
this.indicatorShape,
}) : assert(minWidth != null),
assert(minExtendedWidth != null),
assert(icon != null),
Expand Down Expand Up @@ -562,6 +567,7 @@ class _RailDestination extends StatelessWidget {
final EdgeInsetsGeometry? padding;
final bool useIndicator;
final Color? indicatorColor;
final ShapeBorder? indicatorShape;

final Animation<double> _positionAnimation;

Expand All @@ -573,6 +579,7 @@ class _RailDestination extends StatelessWidget {
);

final bool material3 = Theme.of(context).useMaterial3;
final double indicatorInkOffsetY;

final Widget themedIcon = IconTheme(
data: iconTheme,
Expand All @@ -583,12 +590,13 @@ class _RailDestination extends StatelessWidget {
child: label,
);

final Widget content;
Widget content;

switch (labelType) {
case NavigationRailLabelType.none:
// Split the destination spacing across the top and bottom to keep the icon centered.
final Widget? spacing = material3 ? const SizedBox(height: _verticalDestinationSpacingM3 / 2) : null;
indicatorInkOffsetY = _verticalDestinationPaddingNoLabel - (_verticalIconLabelSpacingM3 / 2);

final Widget iconPart = Column(
children: <Widget>[
Expand All @@ -600,6 +608,7 @@ class _RailDestination extends StatelessWidget {
child: _AddIndicator(
addIndicator: useIndicator,
indicatorColor: indicatorColor,
indicatorShape: indicatorShape,
isCircular: !material3,
indicatorAnimation: destinationAnimation,
child: themedIcon,
Expand Down Expand Up @@ -666,6 +675,7 @@ class _RailDestination extends StatelessWidget {
final Widget topSpacing = SizedBox(height: material3 ? 0 : verticalPadding);
final Widget labelSpacing = SizedBox(height: material3 ? lerpDouble(0, _verticalIconLabelSpacingM3, appearingAnimationValue)! : 0);
final Widget bottomSpacing = SizedBox(height: material3 ? _verticalDestinationSpacingM3 : verticalPadding);
indicatorInkOffsetY = _verticalDestinationPaddingWithLabel;

content = Container(
constraints: BoxConstraints(
Expand All @@ -682,6 +692,7 @@ class _RailDestination extends StatelessWidget {
_AddIndicator(
addIndicator: useIndicator,
indicatorColor: indicatorColor,
indicatorShape: indicatorShape,
isCircular: false,
indicatorAnimation: destinationAnimation,
child: themedIcon,
Expand All @@ -708,6 +719,7 @@ class _RailDestination extends StatelessWidget {
final Widget topSpacing = SizedBox(height: material3 ? 0 : _verticalDestinationPaddingWithLabel);
final Widget labelSpacing = SizedBox(height: material3 ? _verticalIconLabelSpacingM3 : 0);
final Widget bottomSpacing = SizedBox(height: material3 ? _verticalDestinationSpacingM3 : _verticalDestinationPaddingWithLabel);
indicatorInkOffsetY = _verticalDestinationPaddingWithLabel;
content = Container(
constraints: BoxConstraints(
minWidth: minWidth,
Expand All @@ -720,6 +732,7 @@ class _RailDestination extends StatelessWidget {
_AddIndicator(
addIndicator: useIndicator,
indicatorColor: indicatorColor,
indicatorShape: indicatorShape,
isCircular: false,
indicatorAnimation: destinationAnimation,
child: themedIcon,
Expand All @@ -741,14 +754,14 @@ class _RailDestination extends StatelessWidget {
children: <Widget>[
Material(
type: MaterialType.transparency,
child: InkResponse(
child: _IndicatorInkWell(
onTap: onTap,
onHover: (_) {},
highlightShape: BoxShape.rectangle,
borderRadius: material3 ? null : BorderRadius.all(Radius.circular(minWidth / 2.0)),
containedInkWell: true,
borderRadius: BorderRadius.all(Radius.circular(minWidth / 2.0)),
customBorder: indicatorShape,
splashColor: colors.primary.withOpacity(0.12),
hoverColor: colors.primary.withOpacity(0.04),
useMaterial3: material3,
indicatorOffsetY: indicatorInkOffsetY,
child: content,
),
),
Expand All @@ -761,6 +774,43 @@ class _RailDestination extends StatelessWidget {
}
}

class _IndicatorInkWell extends InkResponse {
const _IndicatorInkWell({
super.child,
super.onTap,
ShapeBorder? customBorder,
BorderRadius? borderRadius,
super.splashColor,
super.hoverColor,
required this.useMaterial3,
required this.indicatorOffsetY,
}) : super(
containedInkWell: true,
highlightShape: BoxShape.rectangle,
borderRadius: useMaterial3 ? null : borderRadius,
customBorder: useMaterial3 ? customBorder : null,
);

final bool useMaterial3;
final double indicatorOffsetY;

@override
RectCallback? getRectCallback(RenderBox referenceBox) {
final double indicatorOffsetX = referenceBox.size.width / 2;

if (useMaterial3) {
return () {
return Rect.fromCenter(
center: Offset(indicatorOffsetX, indicatorOffsetY),
width: _kCircularIndicatorDiameter,
height: 32,
);
};
}
return null;
}
}

/// When [addIndicator] is `true`, puts [child] center aligned in a [Stack] with
/// a [NavigationIndicator] behind it, otherwise returns [child].
///
Expand All @@ -771,13 +821,15 @@ class _AddIndicator extends StatelessWidget {
required this.addIndicator,
required this.isCircular,
required this.indicatorColor,
required this.indicatorShape,
required this.indicatorAnimation,
required this.child,
});

final bool addIndicator;
final bool isCircular;
final Color? indicatorColor;
final ShapeBorder? indicatorShape;
final Animation<double> indicatorAnimation;
final Widget child;

Expand All @@ -788,19 +840,18 @@ class _AddIndicator extends StatelessWidget {
}
late final Widget indicator;
if (isCircular) {
const double circularIndicatorDiameter = 56;
indicator = NavigationIndicator(
animation: indicatorAnimation,
height: circularIndicatorDiameter,
width: circularIndicatorDiameter,
borderRadius: BorderRadius.circular(circularIndicatorDiameter / 2),
height: _kCircularIndicatorDiameter,
width: _kCircularIndicatorDiameter,
borderRadius: BorderRadius.circular(_kCircularIndicatorDiameter / 2),
color: indicatorColor,
);
} else {
indicator = NavigationIndicator(
animation: indicatorAnimation,
width: 56,
shape: const StadiumBorder(),
width: _kCircularIndicatorDiameter,
shape: indicatorShape,
color: indicatorColor,
);
}
Expand Down Expand Up @@ -918,16 +969,16 @@ const double _verticalDestinationSpacingM3 = 12.0;
// Hand coded defaults based on Material Design 2.
class _NavigationRailDefaultsM2 extends NavigationRailThemeData {
_NavigationRailDefaultsM2(BuildContext context)
: _theme = Theme.of(context),
_colors = Theme.of(context).colorScheme,
super(
elevation: 0,
groupAlignment: -1,
labelType: NavigationRailLabelType.none,
useIndicator: false,
minWidth: 72.0,
minExtendedWidth: 256,
);
: _theme = Theme.of(context),
_colors = Theme.of(context).colorScheme,
super(
elevation: 0,
groupAlignment: -1,
labelType: NavigationRailLabelType.none,
useIndicator: false,
minWidth: 72.0,
minExtendedWidth: 256,
);

final ThemeData _theme;
final ColorScheme _colors;
Expand Down Expand Up @@ -970,14 +1021,14 @@ class _NavigationRailDefaultsM2 extends NavigationRailThemeData {

class _NavigationRailDefaultsM3 extends NavigationRailThemeData {
_NavigationRailDefaultsM3(this.context)
: super(
elevation: 0.0,
groupAlignment: -1,
labelType: NavigationRailLabelType.none,
useIndicator: true,
minWidth: 80.0,
minExtendedWidth: 256,
);
: super(
elevation: 0.0,
groupAlignment: -1,
labelType: NavigationRailLabelType.none,
useIndicator: true,
minWidth: 80.0,
minExtendedWidth: 256,
);

final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
Expand Down Expand Up @@ -1009,6 +1060,7 @@ class _NavigationRailDefaultsM3 extends NavigationRailThemeData {

@override Color? get indicatorColor => _colors.secondaryContainer;

@override ShapeBorder? get indicatorShape => const StadiumBorder();
}

// END GENERATED TOKEN PROPERTIES - NavigationRail
10 changes: 10 additions & 0 deletions packages/flutter/lib/src/material/navigation_rail_theme.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class NavigationRailThemeData with Diagnosticable {
this.labelType,
this.useIndicator,
this.indicatorColor,
this.indicatorShape,
this.minWidth,
this.minExtendedWidth,
});
Expand Down Expand Up @@ -91,6 +92,9 @@ class NavigationRailThemeData with Diagnosticable {
/// when [useIndicator] is true.
final Color? indicatorColor;

/// Overrides the default shape of the [NavigationRail]'s selection indicator.
final ShapeBorder? indicatorShape;

/// Overrides the default value of [NavigationRail]'s minimum width when it
/// is not extended.
final double? minWidth;
Expand All @@ -112,6 +116,7 @@ class NavigationRailThemeData with Diagnosticable {
NavigationRailLabelType? labelType,
bool? useIndicator,
Color? indicatorColor,
ShapeBorder? indicatorShape,
double? minWidth,
double? minExtendedWidth,
}) {
Expand All @@ -126,6 +131,7 @@ class NavigationRailThemeData with Diagnosticable {
labelType: labelType ?? this.labelType,
useIndicator: useIndicator ?? this.useIndicator,
indicatorColor: indicatorColor ?? this.indicatorColor,
indicatorShape: indicatorShape ?? this.indicatorShape,
minWidth: minWidth ?? this.minWidth,
minExtendedWidth: minExtendedWidth ?? this.minExtendedWidth,
);
Expand All @@ -152,6 +158,7 @@ class NavigationRailThemeData with Diagnosticable {
labelType: t < 0.5 ? a?.labelType : b?.labelType,
useIndicator: t < 0.5 ? a?.useIndicator : b?.useIndicator,
indicatorColor: Color.lerp(a?.indicatorColor, b?.indicatorColor, t),
indicatorShape: ShapeBorder.lerp(a?.indicatorShape, b?.indicatorShape, t),
minWidth: lerpDouble(a?.minWidth, b?.minWidth, t),
minExtendedWidth: lerpDouble(a?.minExtendedWidth, b?.minExtendedWidth, t),

Expand All @@ -170,6 +177,7 @@ class NavigationRailThemeData with Diagnosticable {
labelType,
useIndicator,
indicatorColor,
indicatorShape,
minWidth,
minExtendedWidth,
);
Expand All @@ -193,6 +201,7 @@ class NavigationRailThemeData with Diagnosticable {
&& other.labelType == labelType
&& other.useIndicator == useIndicator
&& other.indicatorColor == indicatorColor
&& other.indicatorShape == indicatorShape
&& other.minWidth == minWidth
&& other.minExtendedWidth == minExtendedWidth;
}
Expand All @@ -212,6 +221,7 @@ class NavigationRailThemeData with Diagnosticable {
properties.add(DiagnosticsProperty<NavigationRailLabelType>('labelType', labelType, defaultValue: defaultData.labelType));
properties.add(DiagnosticsProperty<bool>('useIndicator', useIndicator, defaultValue: defaultData.useIndicator));
properties.add(ColorProperty('indicatorColor', indicatorColor, defaultValue: defaultData.indicatorColor));
properties.add(DiagnosticsProperty<ShapeBorder>('indicatorShape', indicatorShape, defaultValue: null));
properties.add(DoubleProperty('minWidth', minWidth, defaultValue: defaultData.minWidth));
properties.add(DoubleProperty('minExtendedWidth', minExtendedWidth, defaultValue: defaultData.minExtendedWidth));
}
Expand Down
Loading

0 comments on commit beaabb7

Please sign in to comment.