@@ -15,6 +15,8 @@ import 'navigation_rail_theme.dart';
1515import 'text_theme.dart' ;
1616import 'theme.dart' ;
1717
18+ const double _kCircularIndicatorDiameter = 56 ;
19+
1820/// A Material Design widget that is meant to be displayed at the left or right of an
1921/// app to navigate between a small number of views, typically between three and
2022/// five.
@@ -394,6 +396,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
394396 final NavigationRailLabelType labelType = widget.labelType ?? navigationRailTheme.labelType ?? defaults.labelType! ;
395397 final bool useIndicator = widget.useIndicator ?? navigationRailTheme.useIndicator ?? defaults.useIndicator! ;
396398 final Color ? indicatorColor = widget.indicatorColor ?? navigationRailTheme.indicatorColor ?? defaults.indicatorColor;
399+ final ShapeBorder ? indicatorShape = navigationRailTheme.indicatorShape ?? defaults.indicatorShape;
397400
398401 // For backwards compatibility, in M2 the opacity of the unselected icons needs
399402 // to be set to the default if it isn't in the given theme. This can be removed
@@ -443,6 +446,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
443446 padding: widget.destinations[i].padding,
444447 useIndicator: useIndicator,
445448 indicatorColor: useIndicator ? indicatorColor : null ,
449+ indicatorShape: useIndicator ? indicatorShape : null ,
446450 onTap: () {
447451 if (widget.onDestinationSelected != null ) {
448452 widget.onDestinationSelected !(i);
@@ -529,6 +533,7 @@ class _RailDestination extends StatelessWidget {
529533 this .padding,
530534 required this .useIndicator,
531535 this .indicatorColor,
536+ this .indicatorShape,
532537 }) : assert (minWidth != null ),
533538 assert (minExtendedWidth != null ),
534539 assert (icon != null ),
@@ -562,6 +567,7 @@ class _RailDestination extends StatelessWidget {
562567 final EdgeInsetsGeometry ? padding;
563568 final bool useIndicator;
564569 final Color ? indicatorColor;
570+ final ShapeBorder ? indicatorShape;
565571
566572 final Animation <double > _positionAnimation;
567573
@@ -573,6 +579,7 @@ class _RailDestination extends StatelessWidget {
573579 );
574580
575581 final bool material3 = Theme .of (context).useMaterial3;
582+ final double indicatorInkOffsetY;
576583
577584 final Widget themedIcon = IconTheme (
578585 data: iconTheme,
@@ -583,12 +590,13 @@ class _RailDestination extends StatelessWidget {
583590 child: label,
584591 );
585592
586- final Widget content;
593+ Widget content;
587594
588595 switch (labelType) {
589596 case NavigationRailLabelType .none:
590597 // Split the destination spacing across the top and bottom to keep the icon centered.
591598 final Widget ? spacing = material3 ? const SizedBox (height: _verticalDestinationSpacingM3 / 2 ) : null ;
599+ indicatorInkOffsetY = _verticalDestinationPaddingNoLabel - (_verticalIconLabelSpacingM3 / 2 );
592600
593601 final Widget iconPart = Column (
594602 children: < Widget > [
@@ -600,6 +608,7 @@ class _RailDestination extends StatelessWidget {
600608 child: _AddIndicator (
601609 addIndicator: useIndicator,
602610 indicatorColor: indicatorColor,
611+ indicatorShape: indicatorShape,
603612 isCircular: ! material3,
604613 indicatorAnimation: destinationAnimation,
605614 child: themedIcon,
@@ -666,6 +675,7 @@ class _RailDestination extends StatelessWidget {
666675 final Widget topSpacing = SizedBox (height: material3 ? 0 : verticalPadding);
667676 final Widget labelSpacing = SizedBox (height: material3 ? lerpDouble (0 , _verticalIconLabelSpacingM3, appearingAnimationValue)! : 0 );
668677 final Widget bottomSpacing = SizedBox (height: material3 ? _verticalDestinationSpacingM3 : verticalPadding);
678+ indicatorInkOffsetY = _verticalDestinationPaddingWithLabel;
669679
670680 content = Container (
671681 constraints: BoxConstraints (
@@ -682,6 +692,7 @@ class _RailDestination extends StatelessWidget {
682692 _AddIndicator (
683693 addIndicator: useIndicator,
684694 indicatorColor: indicatorColor,
695+ indicatorShape: indicatorShape,
685696 isCircular: false ,
686697 indicatorAnimation: destinationAnimation,
687698 child: themedIcon,
@@ -708,6 +719,7 @@ class _RailDestination extends StatelessWidget {
708719 final Widget topSpacing = SizedBox (height: material3 ? 0 : _verticalDestinationPaddingWithLabel);
709720 final Widget labelSpacing = SizedBox (height: material3 ? _verticalIconLabelSpacingM3 : 0 );
710721 final Widget bottomSpacing = SizedBox (height: material3 ? _verticalDestinationSpacingM3 : _verticalDestinationPaddingWithLabel);
722+ indicatorInkOffsetY = _verticalDestinationPaddingWithLabel;
711723 content = Container (
712724 constraints: BoxConstraints (
713725 minWidth: minWidth,
@@ -720,6 +732,7 @@ class _RailDestination extends StatelessWidget {
720732 _AddIndicator (
721733 addIndicator: useIndicator,
722734 indicatorColor: indicatorColor,
735+ indicatorShape: indicatorShape,
723736 isCircular: false ,
724737 indicatorAnimation: destinationAnimation,
725738 child: themedIcon,
@@ -741,14 +754,14 @@ class _RailDestination extends StatelessWidget {
741754 children: < Widget > [
742755 Material (
743756 type: MaterialType .transparency,
744- child: InkResponse (
757+ child: _IndicatorInkWell (
745758 onTap: onTap,
746- onHover: (_) {},
747- highlightShape: BoxShape .rectangle,
748- borderRadius: material3 ? null : BorderRadius .all (Radius .circular (minWidth / 2.0 )),
749- containedInkWell: true ,
759+ borderRadius: BorderRadius .all (Radius .circular (minWidth / 2.0 )),
760+ customBorder: indicatorShape,
750761 splashColor: colors.primary.withOpacity (0.12 ),
751762 hoverColor: colors.primary.withOpacity (0.04 ),
763+ useMaterial3: material3,
764+ indicatorOffsetY: indicatorInkOffsetY,
752765 child: content,
753766 ),
754767 ),
@@ -761,6 +774,43 @@ class _RailDestination extends StatelessWidget {
761774 }
762775}
763776
777+ class _IndicatorInkWell extends InkResponse {
778+ const _IndicatorInkWell ({
779+ super .child,
780+ super .onTap,
781+ ShapeBorder ? customBorder,
782+ BorderRadius ? borderRadius,
783+ super .splashColor,
784+ super .hoverColor,
785+ required this .useMaterial3,
786+ required this .indicatorOffsetY,
787+ }) : super (
788+ containedInkWell: true ,
789+ highlightShape: BoxShape .rectangle,
790+ borderRadius: useMaterial3 ? null : borderRadius,
791+ customBorder: useMaterial3 ? customBorder : null ,
792+ );
793+
794+ final bool useMaterial3;
795+ final double indicatorOffsetY;
796+
797+ @override
798+ RectCallback ? getRectCallback (RenderBox referenceBox) {
799+ final double indicatorOffsetX = referenceBox.size.width / 2 ;
800+
801+ if (useMaterial3) {
802+ return () {
803+ return Rect .fromCenter (
804+ center: Offset (indicatorOffsetX, indicatorOffsetY),
805+ width: _kCircularIndicatorDiameter,
806+ height: 32 ,
807+ );
808+ };
809+ }
810+ return null ;
811+ }
812+ }
813+
764814/// When [addIndicator] is `true` , puts [child] center aligned in a [Stack] with
765815/// a [NavigationIndicator] behind it, otherwise returns [child] .
766816///
@@ -771,13 +821,15 @@ class _AddIndicator extends StatelessWidget {
771821 required this .addIndicator,
772822 required this .isCircular,
773823 required this .indicatorColor,
824+ required this .indicatorShape,
774825 required this .indicatorAnimation,
775826 required this .child,
776827 });
777828
778829 final bool addIndicator;
779830 final bool isCircular;
780831 final Color ? indicatorColor;
832+ final ShapeBorder ? indicatorShape;
781833 final Animation <double > indicatorAnimation;
782834 final Widget child;
783835
@@ -788,19 +840,18 @@ class _AddIndicator extends StatelessWidget {
788840 }
789841 late final Widget indicator;
790842 if (isCircular) {
791- const double circularIndicatorDiameter = 56 ;
792843 indicator = NavigationIndicator (
793844 animation: indicatorAnimation,
794- height: circularIndicatorDiameter ,
795- width: circularIndicatorDiameter ,
796- borderRadius: BorderRadius .circular (circularIndicatorDiameter / 2 ),
845+ height: _kCircularIndicatorDiameter ,
846+ width: _kCircularIndicatorDiameter ,
847+ borderRadius: BorderRadius .circular (_kCircularIndicatorDiameter / 2 ),
797848 color: indicatorColor,
798849 );
799850 } else {
800851 indicator = NavigationIndicator (
801852 animation: indicatorAnimation,
802- width: 56 ,
803- shape: const StadiumBorder () ,
853+ width: _kCircularIndicatorDiameter ,
854+ shape: indicatorShape ,
804855 color: indicatorColor,
805856 );
806857 }
@@ -918,16 +969,16 @@ const double _verticalDestinationSpacingM3 = 12.0;
918969// Hand coded defaults based on Material Design 2.
919970class _NavigationRailDefaultsM2 extends NavigationRailThemeData {
920971 _NavigationRailDefaultsM2 (BuildContext context)
921- : _theme = Theme .of (context),
922- _colors = Theme .of (context).colorScheme,
923- super (
924- elevation: 0 ,
925- groupAlignment: - 1 ,
926- labelType: NavigationRailLabelType .none,
927- useIndicator: false ,
928- minWidth: 72.0 ,
929- minExtendedWidth: 256 ,
930- );
972+ : _theme = Theme .of (context),
973+ _colors = Theme .of (context).colorScheme,
974+ super (
975+ elevation: 0 ,
976+ groupAlignment: - 1 ,
977+ labelType: NavigationRailLabelType .none,
978+ useIndicator: false ,
979+ minWidth: 72.0 ,
980+ minExtendedWidth: 256 ,
981+ );
931982
932983 final ThemeData _theme;
933984 final ColorScheme _colors;
@@ -970,14 +1021,14 @@ class _NavigationRailDefaultsM2 extends NavigationRailThemeData {
9701021
9711022class _NavigationRailDefaultsM3 extends NavigationRailThemeData {
9721023 _NavigationRailDefaultsM3 (this .context)
973- : super (
974- elevation: 0.0 ,
975- groupAlignment: - 1 ,
976- labelType: NavigationRailLabelType .none,
977- useIndicator: true ,
978- minWidth: 80.0 ,
979- minExtendedWidth: 256 ,
980- );
1024+ : super (
1025+ elevation: 0.0 ,
1026+ groupAlignment: - 1 ,
1027+ labelType: NavigationRailLabelType .none,
1028+ useIndicator: true ,
1029+ minWidth: 80.0 ,
1030+ minExtendedWidth: 256 ,
1031+ );
9811032
9821033 final BuildContext context;
9831034 late final ColorScheme _colors = Theme .of (context).colorScheme;
@@ -1009,6 +1060,7 @@ class _NavigationRailDefaultsM3 extends NavigationRailThemeData {
10091060
10101061 @override Color ? get indicatorColor => _colors.secondaryContainer;
10111062
1063+ @override ShapeBorder ? get indicatorShape => const StadiumBorder ();
10121064}
10131065
10141066// END GENERATED TOKEN PROPERTIES - NavigationRail
0 commit comments