@@ -15,6 +15,8 @@ import 'navigation_rail_theme.dart';
15
15
import 'text_theme.dart' ;
16
16
import 'theme.dart' ;
17
17
18
+ const double _kCircularIndicatorDiameter = 56 ;
19
+
18
20
/// A Material Design widget that is meant to be displayed at the left or right of an
19
21
/// app to navigate between a small number of views, typically between three and
20
22
/// five.
@@ -394,6 +396,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
394
396
final NavigationRailLabelType labelType = widget.labelType ?? navigationRailTheme.labelType ?? defaults.labelType! ;
395
397
final bool useIndicator = widget.useIndicator ?? navigationRailTheme.useIndicator ?? defaults.useIndicator! ;
396
398
final Color ? indicatorColor = widget.indicatorColor ?? navigationRailTheme.indicatorColor ?? defaults.indicatorColor;
399
+ final ShapeBorder ? indicatorShape = navigationRailTheme.indicatorShape ?? defaults.indicatorShape;
397
400
398
401
// For backwards compatibility, in M2 the opacity of the unselected icons needs
399
402
// 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
443
446
padding: widget.destinations[i].padding,
444
447
useIndicator: useIndicator,
445
448
indicatorColor: useIndicator ? indicatorColor : null ,
449
+ indicatorShape: useIndicator ? indicatorShape : null ,
446
450
onTap: () {
447
451
if (widget.onDestinationSelected != null ) {
448
452
widget.onDestinationSelected !(i);
@@ -529,6 +533,7 @@ class _RailDestination extends StatelessWidget {
529
533
this .padding,
530
534
required this .useIndicator,
531
535
this .indicatorColor,
536
+ this .indicatorShape,
532
537
}) : assert (minWidth != null ),
533
538
assert (minExtendedWidth != null ),
534
539
assert (icon != null ),
@@ -562,6 +567,7 @@ class _RailDestination extends StatelessWidget {
562
567
final EdgeInsetsGeometry ? padding;
563
568
final bool useIndicator;
564
569
final Color ? indicatorColor;
570
+ final ShapeBorder ? indicatorShape;
565
571
566
572
final Animation <double > _positionAnimation;
567
573
@@ -573,6 +579,7 @@ class _RailDestination extends StatelessWidget {
573
579
);
574
580
575
581
final bool material3 = Theme .of (context).useMaterial3;
582
+ final double indicatorInkOffsetY;
576
583
577
584
final Widget themedIcon = IconTheme (
578
585
data: iconTheme,
@@ -583,12 +590,13 @@ class _RailDestination extends StatelessWidget {
583
590
child: label,
584
591
);
585
592
586
- final Widget content;
593
+ Widget content;
587
594
588
595
switch (labelType) {
589
596
case NavigationRailLabelType .none:
590
597
// Split the destination spacing across the top and bottom to keep the icon centered.
591
598
final Widget ? spacing = material3 ? const SizedBox (height: _verticalDestinationSpacingM3 / 2 ) : null ;
599
+ indicatorInkOffsetY = _verticalDestinationPaddingNoLabel - (_verticalIconLabelSpacingM3 / 2 );
592
600
593
601
final Widget iconPart = Column (
594
602
children: < Widget > [
@@ -600,6 +608,7 @@ class _RailDestination extends StatelessWidget {
600
608
child: _AddIndicator (
601
609
addIndicator: useIndicator,
602
610
indicatorColor: indicatorColor,
611
+ indicatorShape: indicatorShape,
603
612
isCircular: ! material3,
604
613
indicatorAnimation: destinationAnimation,
605
614
child: themedIcon,
@@ -666,6 +675,7 @@ class _RailDestination extends StatelessWidget {
666
675
final Widget topSpacing = SizedBox (height: material3 ? 0 : verticalPadding);
667
676
final Widget labelSpacing = SizedBox (height: material3 ? lerpDouble (0 , _verticalIconLabelSpacingM3, appearingAnimationValue)! : 0 );
668
677
final Widget bottomSpacing = SizedBox (height: material3 ? _verticalDestinationSpacingM3 : verticalPadding);
678
+ indicatorInkOffsetY = _verticalDestinationPaddingWithLabel;
669
679
670
680
content = Container (
671
681
constraints: BoxConstraints (
@@ -682,6 +692,7 @@ class _RailDestination extends StatelessWidget {
682
692
_AddIndicator (
683
693
addIndicator: useIndicator,
684
694
indicatorColor: indicatorColor,
695
+ indicatorShape: indicatorShape,
685
696
isCircular: false ,
686
697
indicatorAnimation: destinationAnimation,
687
698
child: themedIcon,
@@ -708,6 +719,7 @@ class _RailDestination extends StatelessWidget {
708
719
final Widget topSpacing = SizedBox (height: material3 ? 0 : _verticalDestinationPaddingWithLabel);
709
720
final Widget labelSpacing = SizedBox (height: material3 ? _verticalIconLabelSpacingM3 : 0 );
710
721
final Widget bottomSpacing = SizedBox (height: material3 ? _verticalDestinationSpacingM3 : _verticalDestinationPaddingWithLabel);
722
+ indicatorInkOffsetY = _verticalDestinationPaddingWithLabel;
711
723
content = Container (
712
724
constraints: BoxConstraints (
713
725
minWidth: minWidth,
@@ -720,6 +732,7 @@ class _RailDestination extends StatelessWidget {
720
732
_AddIndicator (
721
733
addIndicator: useIndicator,
722
734
indicatorColor: indicatorColor,
735
+ indicatorShape: indicatorShape,
723
736
isCircular: false ,
724
737
indicatorAnimation: destinationAnimation,
725
738
child: themedIcon,
@@ -741,14 +754,14 @@ class _RailDestination extends StatelessWidget {
741
754
children: < Widget > [
742
755
Material (
743
756
type: MaterialType .transparency,
744
- child: InkResponse (
757
+ child: _IndicatorInkWell (
745
758
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,
750
761
splashColor: colors.primary.withOpacity (0.12 ),
751
762
hoverColor: colors.primary.withOpacity (0.04 ),
763
+ useMaterial3: material3,
764
+ indicatorOffsetY: indicatorInkOffsetY,
752
765
child: content,
753
766
),
754
767
),
@@ -761,6 +774,43 @@ class _RailDestination extends StatelessWidget {
761
774
}
762
775
}
763
776
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
+
764
814
/// When [addIndicator] is `true` , puts [child] center aligned in a [Stack] with
765
815
/// a [NavigationIndicator] behind it, otherwise returns [child] .
766
816
///
@@ -771,13 +821,15 @@ class _AddIndicator extends StatelessWidget {
771
821
required this .addIndicator,
772
822
required this .isCircular,
773
823
required this .indicatorColor,
824
+ required this .indicatorShape,
774
825
required this .indicatorAnimation,
775
826
required this .child,
776
827
});
777
828
778
829
final bool addIndicator;
779
830
final bool isCircular;
780
831
final Color ? indicatorColor;
832
+ final ShapeBorder ? indicatorShape;
781
833
final Animation <double > indicatorAnimation;
782
834
final Widget child;
783
835
@@ -788,19 +840,18 @@ class _AddIndicator extends StatelessWidget {
788
840
}
789
841
late final Widget indicator;
790
842
if (isCircular) {
791
- const double circularIndicatorDiameter = 56 ;
792
843
indicator = NavigationIndicator (
793
844
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 ),
797
848
color: indicatorColor,
798
849
);
799
850
} else {
800
851
indicator = NavigationIndicator (
801
852
animation: indicatorAnimation,
802
- width: 56 ,
803
- shape: const StadiumBorder () ,
853
+ width: _kCircularIndicatorDiameter ,
854
+ shape: indicatorShape ,
804
855
color: indicatorColor,
805
856
);
806
857
}
@@ -918,16 +969,16 @@ const double _verticalDestinationSpacingM3 = 12.0;
918
969
// Hand coded defaults based on Material Design 2.
919
970
class _NavigationRailDefaultsM2 extends NavigationRailThemeData {
920
971
_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
+ );
931
982
932
983
final ThemeData _theme;
933
984
final ColorScheme _colors;
@@ -970,14 +1021,14 @@ class _NavigationRailDefaultsM2 extends NavigationRailThemeData {
970
1021
971
1022
class _NavigationRailDefaultsM3 extends NavigationRailThemeData {
972
1023
_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
+ );
981
1032
982
1033
final BuildContext context;
983
1034
late final ColorScheme _colors = Theme .of (context).colorScheme;
@@ -1009,6 +1060,7 @@ class _NavigationRailDefaultsM3 extends NavigationRailThemeData {
1009
1060
1010
1061
@override Color ? get indicatorColor => _colors.secondaryContainer;
1011
1062
1063
+ @override ShapeBorder ? get indicatorShape => const StadiumBorder ();
1012
1064
}
1013
1065
1014
1066
// END GENERATED TOKEN PROPERTIES - NavigationRail
0 commit comments