@@ -346,7 +346,7 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
346346 late List <AnimationController > _destinationControllers;
347347 late List <Animation <double >> _destinationAnimations;
348348 late AnimationController _extendedController;
349- late Animation < double > _extendedAnimation;
349+ late CurvedAnimation _extendedAnimation;
350350
351351 @override
352352 void initState () {
@@ -488,6 +488,8 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
488488 controller.dispose ();
489489 }
490490 _extendedController.dispose ();
491+ _extendedAnimation.dispose ();
492+
491493 }
492494
493495 void _initControllers () {
@@ -528,8 +530,8 @@ class _NavigationRailState extends State<NavigationRail> with TickerProviderStat
528530 }
529531}
530532
531- class _RailDestination extends StatelessWidget {
532- _RailDestination ({
533+ class _RailDestination extends StatefulWidget {
534+ const _RailDestination ({
533535 required this .minWidth,
534536 required this .minExtendedWidth,
535537 required this .icon,
@@ -547,11 +549,7 @@ class _RailDestination extends StatelessWidget {
547549 this .indicatorColor,
548550 this .indicatorShape,
549551 this .disabled = false ,
550- }) : _positionAnimation = CurvedAnimation (
551- parent: ReverseAnimation (destinationAnimation),
552- curve: Curves .easeInOut,
553- reverseCurve: Curves .easeInOut.flipped,
554- );
552+ });
555553
556554 final double minWidth;
557555 final double minExtendedWidth;
@@ -571,111 +569,148 @@ class _RailDestination extends StatelessWidget {
571569 final ShapeBorder ? indicatorShape;
572570 final bool disabled;
573571
574- final Animation <double > _positionAnimation;
572+
573+ @override
574+ State <_RailDestination > createState () => _RailDestinationState ();
575+ }
576+
577+ class _RailDestinationState extends State <_RailDestination > {
578+ late CurvedAnimation _positionAnimation;
579+
580+ @override
581+ void initState () {
582+ super .initState ();
583+ _setPositionAnimation ();
584+ }
585+
586+ @override
587+ void didUpdateWidget (_RailDestination oldWidget) {
588+ super .didUpdateWidget (oldWidget);
589+ if (widget.destinationAnimation != oldWidget.destinationAnimation) {
590+ _positionAnimation.dispose ();
591+ _setPositionAnimation ();
592+ }
593+ }
594+
595+ void _setPositionAnimation () {
596+ _positionAnimation = CurvedAnimation (
597+ parent: ReverseAnimation (widget.destinationAnimation),
598+ curve: Curves .easeInOut,
599+ reverseCurve: Curves .easeInOut.flipped,
600+ );
601+ }
602+
603+ @override
604+ void dispose () {
605+ _positionAnimation.dispose ();
606+ super .dispose ();
607+ }
608+
609+
575610
576611 @override
577612 Widget build (BuildContext context) {
578613 assert (
579- useIndicator || indicatorColor == null ,
614+ widget. useIndicator || widget. indicatorColor == null ,
580615 '[NavigationRail.indicatorColor] does not have an effect when [NavigationRail.useIndicator] is false' ,
581616 );
582617
583618 final ThemeData theme = Theme .of (context);
584619 final TextDirection textDirection = Directionality .of (context);
585620 final bool material3 = theme.useMaterial3;
586- final EdgeInsets destinationPadding = (padding ?? EdgeInsets .zero).resolve (textDirection);
621+ final EdgeInsets destinationPadding = (widget. padding ?? EdgeInsets .zero).resolve (textDirection);
587622 Offset indicatorOffset;
588623 bool applyXOffset = false ;
589624
590625 final Widget themedIcon = IconTheme (
591- data: disabled
592- ? iconTheme.copyWith (color: theme.colorScheme.onSurface.withOpacity (0.38 ))
593- : iconTheme,
594- child: icon,
626+ data: widget. disabled
627+ ? widget. iconTheme.copyWith (color: theme.colorScheme.onSurface.withOpacity (0.38 ))
628+ : widget. iconTheme,
629+ child: widget. icon,
595630 );
596631 final Widget styledLabel = DefaultTextStyle (
597- style: disabled
598- ? labelTextStyle.copyWith (color: theme.colorScheme.onSurface.withOpacity (0.38 ))
599- : labelTextStyle,
600- child: label,
632+ style: widget. disabled
633+ ? widget. labelTextStyle.copyWith (color: theme.colorScheme.onSurface.withOpacity (0.38 ))
634+ : widget. labelTextStyle,
635+ child: widget. label,
601636 );
602637
603638 Widget content;
604639
605640 // The indicator height is fixed and equal to _kIndicatorHeight.
606641 // When the icon height is larger than the indicator height the indicator
607642 // vertical offset is used to vertically center the indicator.
608- final bool isLargeIconSize = iconTheme.size != null && iconTheme.size! > _kIndicatorHeight;
609- final double indicatorVerticalOffset = isLargeIconSize ? (iconTheme.size! - _kIndicatorHeight) / 2 : 0 ;
643+ final bool isLargeIconSize = widget. iconTheme.size != null && widget. iconTheme.size! > _kIndicatorHeight;
644+ final double indicatorVerticalOffset = isLargeIconSize ? (widget. iconTheme.size! - _kIndicatorHeight) / 2 : 0 ;
610645
611- switch (labelType) {
646+ switch (widget. labelType) {
612647 case NavigationRailLabelType .none:
613648 // Split the destination spacing across the top and bottom to keep the icon centered.
614649 final Widget ? spacing = material3 ? const SizedBox (height: _verticalDestinationSpacingM3 / 2 ) : null ;
615650 indicatorOffset = Offset (
616- minWidth / 2 + destinationPadding.left,
651+ widget. minWidth / 2 + destinationPadding.left,
617652 _verticalDestinationSpacingM3 / 2 + destinationPadding.top + indicatorVerticalOffset,
618653 );
619654 final Widget iconPart = Column (
620655 children: < Widget > [
621656 if (spacing != null ) spacing,
622657 SizedBox (
623- width: minWidth,
624- height: material3 ? null : minWidth,
658+ width: widget. minWidth,
659+ height: material3 ? null : widget. minWidth,
625660 child: Center (
626661 child: _AddIndicator (
627- addIndicator: useIndicator,
628- indicatorColor: indicatorColor,
629- indicatorShape: indicatorShape,
662+ addIndicator: widget. useIndicator,
663+ indicatorColor: widget. indicatorColor,
664+ indicatorShape: widget. indicatorShape,
630665 isCircular: ! material3,
631- indicatorAnimation: destinationAnimation,
666+ indicatorAnimation: widget. destinationAnimation,
632667 child: themedIcon,
633668 ),
634669 ),
635670 ),
636671 if (spacing != null ) spacing,
637672 ],
638673 );
639- if (extendedTransitionAnimation.value == 0 ) {
674+ if (widget. extendedTransitionAnimation.value == 0 ) {
640675 content = Padding (
641- padding: padding ?? EdgeInsets .zero,
676+ padding: widget. padding ?? EdgeInsets .zero,
642677 child: Stack (
643678 children: < Widget > [
644679 iconPart,
645680 // For semantics when label is not showing,
646681 SizedBox .shrink (
647682 child: Visibility .maintain (
648683 visible: false ,
649- child: label,
684+ child: widget. label,
650685 ),
651686 ),
652687 ],
653688 ),
654689 );
655690 } else {
656- final Animation <double > labelFadeAnimation = extendedTransitionAnimation.drive (CurveTween (curve: const Interval (0.0 , 0.25 )));
691+ final Animation <double > labelFadeAnimation = widget. extendedTransitionAnimation.drive (CurveTween (curve: const Interval (0.0 , 0.25 )));
657692 applyXOffset = true ;
658693 content = Padding (
659- padding: padding ?? EdgeInsets .zero,
694+ padding: widget. padding ?? EdgeInsets .zero,
660695 child: ConstrainedBox (
661696 constraints: BoxConstraints (
662- minWidth: lerpDouble (minWidth, minExtendedWidth, extendedTransitionAnimation.value)! ,
697+ minWidth: lerpDouble (widget. minWidth, widget. minExtendedWidth, widget. extendedTransitionAnimation.value)! ,
663698 ),
664699 child: ClipRect (
665700 child: Row (
666701 children: < Widget > [
667702 iconPart,
668703 Align (
669704 heightFactor: 1.0 ,
670- widthFactor: extendedTransitionAnimation.value,
705+ widthFactor: widget. extendedTransitionAnimation.value,
671706 alignment: AlignmentDirectional .centerStart,
672707 child: FadeTransition (
673708 alwaysIncludeSemantics: true ,
674709 opacity: labelFadeAnimation,
675710 child: styledLabel,
676711 ),
677712 ),
678- SizedBox (width: _horizontalDestinationPadding * extendedTransitionAnimation.value),
713+ SizedBox (width: _horizontalDestinationPadding * widget. extendedTransitionAnimation.value),
679714 ],
680715 ),
681716 ),
@@ -685,42 +720,42 @@ class _RailDestination extends StatelessWidget {
685720 case NavigationRailLabelType .selected:
686721 final double appearingAnimationValue = 1 - _positionAnimation.value;
687722 final double verticalPadding = lerpDouble (_verticalDestinationPaddingNoLabel, _verticalDestinationPaddingWithLabel, appearingAnimationValue)! ;
688- final Interval interval = selected ? const Interval (0.25 , 0.75 ) : const Interval (0.75 , 1.0 );
689- final Animation <double > labelFadeAnimation = destinationAnimation.drive (CurveTween (curve: interval));
690- final double minHeight = material3 ? 0 : minWidth;
723+ final Interval interval = widget. selected ? const Interval (0.25 , 0.75 ) : const Interval (0.75 , 1.0 );
724+ final Animation <double > labelFadeAnimation = widget. destinationAnimation.drive (CurveTween (curve: interval));
725+ final double minHeight = material3 ? 0 : widget. minWidth;
691726 final Widget topSpacing = SizedBox (height: material3 ? 0 : verticalPadding);
692727 final Widget labelSpacing = SizedBox (height: material3 ? lerpDouble (0 , _verticalIconLabelSpacingM3, appearingAnimationValue)! : 0 );
693728 final Widget bottomSpacing = SizedBox (height: material3 ? _verticalDestinationSpacingM3 : verticalPadding);
694729 final double indicatorHorizontalPadding = (destinationPadding.left / 2 ) - (destinationPadding.right / 2 );
695730 final double indicatorVerticalPadding = destinationPadding.top;
696731 indicatorOffset = Offset (
697- minWidth / 2 + indicatorHorizontalPadding,
732+ widget. minWidth / 2 + indicatorHorizontalPadding,
698733 indicatorVerticalPadding + indicatorVerticalOffset,
699734 );
700- if (minWidth < _NavigationRailDefaultsM2 (context).minWidth! ) {
735+ if (widget. minWidth < _NavigationRailDefaultsM2 (context).minWidth! ) {
701736 indicatorOffset = Offset (
702- minWidth / 2 + _horizontalDestinationSpacingM3,
737+ widget. minWidth / 2 + _horizontalDestinationSpacingM3,
703738 indicatorVerticalPadding + indicatorVerticalOffset,
704739 );
705740 }
706741 content = Container (
707742 constraints: BoxConstraints (
708- minWidth: minWidth,
743+ minWidth: widget. minWidth,
709744 minHeight: minHeight,
710745 ),
711- padding: padding ?? const EdgeInsets .symmetric (horizontal: _horizontalDestinationPadding),
746+ padding: widget. padding ?? const EdgeInsets .symmetric (horizontal: _horizontalDestinationPadding),
712747 child: ClipRect (
713748 child: Column (
714749 mainAxisSize: MainAxisSize .min,
715750 mainAxisAlignment: MainAxisAlignment .center,
716751 children: < Widget > [
717752 topSpacing,
718753 _AddIndicator (
719- addIndicator: useIndicator,
720- indicatorColor: indicatorColor,
721- indicatorShape: indicatorShape,
754+ addIndicator: widget. useIndicator,
755+ indicatorColor: widget. indicatorColor,
756+ indicatorShape: widget. indicatorShape,
722757 isCircular: false ,
723- indicatorAnimation: destinationAnimation,
758+ indicatorAnimation: widget. destinationAnimation,
724759 child: themedIcon,
725760 ),
726761 labelSpacing,
@@ -740,37 +775,37 @@ class _RailDestination extends StatelessWidget {
740775 ),
741776 );
742777 case NavigationRailLabelType .all:
743- final double minHeight = material3 ? 0 : minWidth;
778+ final double minHeight = material3 ? 0 : widget. minWidth;
744779 final Widget topSpacing = SizedBox (height: material3 ? 0 : _verticalDestinationPaddingWithLabel);
745780 final Widget labelSpacing = SizedBox (height: material3 ? _verticalIconLabelSpacingM3 : 0 );
746781 final Widget bottomSpacing = SizedBox (height: material3 ? _verticalDestinationSpacingM3 : _verticalDestinationPaddingWithLabel);
747782 final double indicatorHorizontalPadding = (destinationPadding.left / 2 ) - (destinationPadding.right / 2 );
748783 final double indicatorVerticalPadding = destinationPadding.top;
749784 indicatorOffset = Offset (
750- minWidth / 2 + indicatorHorizontalPadding,
785+ widget. minWidth / 2 + indicatorHorizontalPadding,
751786 indicatorVerticalPadding + indicatorVerticalOffset,
752787 );
753- if (minWidth < _NavigationRailDefaultsM2 (context).minWidth! ) {
788+ if (widget. minWidth < _NavigationRailDefaultsM2 (context).minWidth! ) {
754789 indicatorOffset = Offset (
755- minWidth / 2 + _horizontalDestinationSpacingM3,
790+ widget. minWidth / 2 + _horizontalDestinationSpacingM3,
756791 indicatorVerticalPadding + indicatorVerticalOffset,
757792 );
758793 }
759794 content = Container (
760795 constraints: BoxConstraints (
761- minWidth: minWidth,
796+ minWidth: widget. minWidth,
762797 minHeight: minHeight,
763798 ),
764- padding: padding ?? const EdgeInsets .symmetric (horizontal: _horizontalDestinationPadding),
799+ padding: widget. padding ?? const EdgeInsets .symmetric (horizontal: _horizontalDestinationPadding),
765800 child: Column (
766801 children: < Widget > [
767802 topSpacing,
768803 _AddIndicator (
769- addIndicator: useIndicator,
770- indicatorColor: indicatorColor,
771- indicatorShape: indicatorShape,
804+ addIndicator: widget. useIndicator,
805+ indicatorColor: widget. indicatorColor,
806+ indicatorShape: widget. indicatorShape,
772807 isCircular: false ,
773- indicatorAnimation: destinationAnimation,
808+ indicatorAnimation: widget. destinationAnimation,
774809 child: themedIcon,
775810 ),
776811 labelSpacing,
@@ -791,15 +826,15 @@ class _RailDestination extends StatelessWidget {
791826 : colors.primary.withOpacity (0.04 );
792827 return Semantics (
793828 container: true ,
794- selected: selected,
829+ selected: widget. selected,
795830 child: Stack (
796831 children: < Widget > [
797832 Material (
798833 type: MaterialType .transparency,
799834 child: _IndicatorInkWell (
800- onTap: disabled ? null : onTap,
801- borderRadius: BorderRadius .all (Radius .circular (minWidth / 2.0 )),
802- customBorder: indicatorShape,
835+ onTap: widget. disabled ? null : widget. onTap,
836+ borderRadius: BorderRadius .all (Radius .circular (widget. minWidth / 2.0 )),
837+ customBorder: widget. indicatorShape,
803838 splashColor: effectiveSplashColor,
804839 hoverColor: effectiveHoverColor,
805840 useMaterial3: material3,
@@ -810,7 +845,7 @@ class _RailDestination extends StatelessWidget {
810845 ),
811846 ),
812847 Semantics (
813- label: indexLabel,
848+ label: widget. indexLabel,
814849 ),
815850 ],
816851 ),
0 commit comments