@@ -728,6 +728,7 @@ class _CupertinoNavigationBarState extends State<CupertinoNavigationBar> {
728728 userTrailing: widget.trailing,
729729 padding: widget.padding,
730730 userLargeTitle: widget.largeTitle,
731+ userBottom: widget.bottom,
731732 large: widget.largeTitle != null ,
732733 staticBar: true , // This one does not scroll
733734 context: context,
@@ -764,7 +765,8 @@ class _CupertinoNavigationBarState extends State<CupertinoNavigationBar> {
764765 ),
765766 ),
766767 ),
767- if (widget.bottom != null ) SizedBox (height: bottomHeight, child: widget.bottom),
768+ if (widget.bottom != null )
769+ SizedBox (height: bottomHeight, child: components.navBarBottom),
768770 ],
769771 ),
770772 );
@@ -775,7 +777,8 @@ class _CupertinoNavigationBarState extends State<CupertinoNavigationBar> {
775777 child: Column (
776778 children: < Widget > [
777779 navBar,
778- if (widget.bottom != null ) SizedBox (height: bottomHeight, child: widget.bottom),
780+ if (widget.bottom != null )
781+ SizedBox (height: bottomHeight, child: components.navBarBottom),
779782 ],
780783 ),
781784 );
@@ -1265,6 +1268,23 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
12651268 ? Visibility (visible: ! searchIsActive, child: widget.trailing! )
12661269 : null ,
12671270 userLargeTitle: widget.largeTitle,
1271+ userBottom:
1272+ (widget._searchable
1273+ ? searchIsActive
1274+ ? _ActiveSearchableBottom (
1275+ animationController: _animationController,
1276+ animation: persistentHeightAnimation,
1277+ searchField: widget.searchField,
1278+ onSearchFieldTap: _onSearchFieldTap,
1279+ )
1280+ : _InactiveSearchableBottom (
1281+ animationController: _animationController,
1282+ animation: persistentHeightAnimation,
1283+ searchField: preferredSizeSearchField,
1284+ onSearchFieldTap: _onSearchFieldTap,
1285+ )
1286+ : widget.bottom) ??
1287+ const SizedBox .shrink (),
12681288 padding: widget.padding,
12691289 large: true ,
12701290 staticBar: false , // This one scrolls.
@@ -1297,23 +1317,6 @@ class _CupertinoSliverNavigationBarState extends State<CupertinoSliverNavigation
12971317 stretchConfiguration:
12981318 widget.stretch && ! searchIsActive ? OverScrollHeaderStretchConfiguration () : null ,
12991319 enableBackgroundFilterBlur: widget.enableBackgroundFilterBlur,
1300- bottom:
1301- (widget._searchable
1302- ? searchIsActive
1303- ? _ActiveSearchableBottom (
1304- animationController: _animationController,
1305- animation: persistentHeightAnimation,
1306- searchField: widget.searchField,
1307- onSearchFieldTap: _onSearchFieldTap,
1308- )
1309- : _InactiveSearchableBottom (
1310- animationController: _animationController,
1311- animation: persistentHeightAnimation,
1312- searchField: preferredSizeSearchField,
1313- onSearchFieldTap: _onSearchFieldTap,
1314- )
1315- : widget.bottom) ??
1316- const SizedBox .shrink (),
13171320 bottomMode:
13181321 searchIsActive
13191322 ? NavigationBarBottomMode .always
@@ -1347,7 +1350,6 @@ class _LargeTitleNavigationBarSliverDelegate extends SliverPersistentHeaderDeleg
13471350 required this .alwaysShowMiddle,
13481351 required this .stretchConfiguration,
13491352 required this .enableBackgroundFilterBlur,
1350- required this .bottom,
13511353 required this .bottomMode,
13521354 required this .bottomHeight,
13531355 required this .controller,
@@ -1368,7 +1370,6 @@ class _LargeTitleNavigationBarSliverDelegate extends SliverPersistentHeaderDeleg
13681370 final double largeTitleHeight;
13691371 final bool alwaysShowMiddle;
13701372 final bool enableBackgroundFilterBlur;
1371- final Widget bottom;
13721373 final NavigationBarBottomMode bottomMode;
13731374 final double bottomHeight;
13741375 final AnimationController controller;
@@ -1482,14 +1483,14 @@ class _LargeTitleNavigationBarSliverDelegate extends SliverPersistentHeaderDeleg
14821483 bottom: 0.0 ,
14831484 child: SizedBox (
14841485 height: bottomHeight * (1.0 - bottomShrinkFactor),
1485- child: ClipRect (child: bottom ),
1486+ child: ClipRect (child: components.navBarBottom ),
14861487 ),
14871488 ),
14881489 ],
14891490 ),
14901491 ),
14911492 if (bottomMode == NavigationBarBottomMode .always)
1492- SizedBox (height: bottomHeight, child: bottom ),
1493+ SizedBox (height: bottomHeight, child: components.navBarBottom ),
14931494 ],
14941495 ),
14951496 ),
@@ -1537,7 +1538,6 @@ class _LargeTitleNavigationBarSliverDelegate extends SliverPersistentHeaderDeleg
15371538 alwaysShowMiddle != oldDelegate.alwaysShowMiddle ||
15381539 heroTag != oldDelegate.heroTag ||
15391540 enableBackgroundFilterBlur != oldDelegate.enableBackgroundFilterBlur ||
1540- bottom != oldDelegate.bottom ||
15411541 bottomMode != oldDelegate.bottomMode ||
15421542 bottomHeight != oldDelegate.bottomHeight ||
15431543 controller != oldDelegate.controller;
@@ -1775,7 +1775,8 @@ class _NavigationBarStaticComponentsKeys {
17751775 backLabelKey = GlobalKey (debugLabel: 'Back label' ),
17761776 middleKey = GlobalKey (debugLabel: 'Middle' ),
17771777 trailingKey = GlobalKey (debugLabel: 'Trailing' ),
1778- largeTitleKey = GlobalKey (debugLabel: 'Large title' );
1778+ largeTitleKey = GlobalKey (debugLabel: 'Large title' ),
1779+ navBarBottomKey = GlobalKey (debugLabel: 'Navigation bar bottom' );
17791780
17801781 final GlobalKey navBarBoxKey;
17811782 final GlobalKey leadingKey;
@@ -1784,6 +1785,7 @@ class _NavigationBarStaticComponentsKeys {
17841785 final GlobalKey middleKey;
17851786 final GlobalKey trailingKey;
17861787 final GlobalKey largeTitleKey;
1788+ final GlobalKey navBarBottomKey;
17871789}
17881790
17891791// Based on various user Widgets and other parameters, construct KeyedSubtree
@@ -1802,6 +1804,7 @@ class _NavigationBarStaticComponents {
18021804 required Widget ? userMiddle,
18031805 required Widget ? userTrailing,
18041806 required Widget ? userLargeTitle,
1807+ required Widget ? userBottom,
18051808 required EdgeInsetsDirectional ? padding,
18061809 required bool large,
18071810 required bool staticBar,
@@ -1847,6 +1850,10 @@ class _NavigationBarStaticComponents {
18471850 route: route,
18481851 automaticImplyTitle: automaticallyImplyTitle,
18491852 large: large,
1853+ ),
1854+ navBarBottom = createNavBarBottom (
1855+ navBarBottomKey: keys.navBarBottomKey,
1856+ userBottom: userBottom,
18501857 );
18511858
18521859 static Widget ? _derivedTitle ({
@@ -2024,6 +2031,14 @@ class _NavigationBarStaticComponents {
20242031
20252032 return KeyedSubtree (key: largeTitleKey, child: largeTitleContent! );
20262033 }
2034+
2035+ final KeyedSubtree ? navBarBottom;
2036+ static KeyedSubtree ? createNavBarBottom ({
2037+ required GlobalKey navBarBottomKey,
2038+ required Widget ? userBottom,
2039+ }) {
2040+ return KeyedSubtree (key: navBarBottomKey, child: userBottom ?? const SizedBox .shrink ());
2041+ }
20272042}
20282043
20292044/// A nav bar back button typically used in [CupertinoNavigationBar] .
@@ -2517,13 +2532,15 @@ class _NavigationBarTransition extends StatelessWidget {
25172532 if (componentsTransition.bottomMiddle != null ) componentsTransition.bottomMiddle! ,
25182533 if (componentsTransition.bottomLargeTitle != null ) componentsTransition.bottomLargeTitle! ,
25192534 if (componentsTransition.bottomTrailing != null ) componentsTransition.bottomTrailing! ,
2535+ if (componentsTransition.bottomNavBarBottom != null ) componentsTransition.bottomNavBarBottom! ,
25202536 // Draw top components on top of the bottom components.
25212537 if (componentsTransition.topLeading != null ) componentsTransition.topLeading! ,
25222538 if (componentsTransition.topBackChevron != null ) componentsTransition.topBackChevron! ,
25232539 if (componentsTransition.topBackLabel != null ) componentsTransition.topBackLabel! ,
25242540 if (componentsTransition.topMiddle != null ) componentsTransition.topMiddle! ,
25252541 if (componentsTransition.topLargeTitle != null ) componentsTransition.topLargeTitle! ,
25262542 if (componentsTransition.topTrailing != null ) componentsTransition.topTrailing! ,
2543+ if (componentsTransition.topNavBarBottom != null ) componentsTransition.topNavBarBottom! ,
25272544 ];
25282545
25292546 // The actual outer box is big enough to contain both the bottom and top
@@ -2897,6 +2914,39 @@ class _NavigationBarComponentsTransition {
28972914 );
28982915 }
28992916
2917+ Widget ? get bottomNavBarBottom {
2918+ final KeyedSubtree ? bottomNavBarBottom =
2919+ bottomComponents.navBarBottomKey.currentWidget as KeyedSubtree ? ;
2920+ final KeyedSubtree ? topNavBarBottom =
2921+ topComponents.navBarBottomKey.currentWidget as KeyedSubtree ? ;
2922+
2923+ if (bottomNavBarBottom == null ) {
2924+ return null ;
2925+ }
2926+
2927+ final RelativeRect from = positionInTransitionBox (
2928+ bottomComponents.navBarBottomKey,
2929+ from: bottomNavBarBox,
2930+ );
2931+ // Shift in from the leading edge of the screen.
2932+ final RelativeRectTween positionTween = RelativeRectTween (
2933+ begin: from,
2934+ end: from.shift (Offset (- forwardDirection * bottomNavBarBox.size.width, 0.0 )),
2935+ );
2936+
2937+ Widget child = bottomNavBarBottom.child;
2938+
2939+ // Fade out only if this is not a CupertinoSliverNavigationBar.search to
2940+ // CupertinoSliverNavigationBar.search transition.
2941+ if (topNavBarBottom == null ||
2942+ topNavBarBottom.child is ! _InactiveSearchableBottom ||
2943+ bottomNavBarBottom.child is ! _InactiveSearchableBottom ) {
2944+ child = FadeTransition (opacity: fadeOutBy (0.8 ), child: child);
2945+ }
2946+
2947+ return PositionedTransition (rect: animation.drive (positionTween), child: child);
2948+ }
2949+
29002950 Widget ? get topLeading {
29012951 final KeyedSubtree ? topLeading = topComponents.leadingKey.currentWidget as KeyedSubtree ? ;
29022952
@@ -3093,7 +3143,7 @@ class _NavigationBarComponentsTransition {
30933143 return PositionedTransition (
30943144 rect: animation.drive (positionTween),
30953145 child: FadeTransition (
3096- opacity: fadeInFrom (0.3 ),
3146+ opacity: fadeInFrom (0.0 ),
30973147 child: DefaultTextStyle (
30983148 style: topLargeTitleTextStyle! ,
30993149 maxLines: 1 ,
@@ -3103,6 +3153,39 @@ class _NavigationBarComponentsTransition {
31033153 ),
31043154 );
31053155 }
3156+
3157+ Widget ? get topNavBarBottom {
3158+ final KeyedSubtree ? topNavBarBottom =
3159+ topComponents.navBarBottomKey.currentWidget as KeyedSubtree ? ;
3160+ final KeyedSubtree ? bottomNavBarBottom =
3161+ bottomComponents.navBarBottomKey.currentWidget as KeyedSubtree ? ;
3162+
3163+ if (topNavBarBottom == null ) {
3164+ return null ;
3165+ }
3166+
3167+ final RelativeRect to = positionInTransitionBox (
3168+ topComponents.navBarBottomKey,
3169+ from: topNavBarBox,
3170+ );
3171+ // Shift in from the trailing edge of the screen.
3172+ final RelativeRectTween positionTween = RelativeRectTween (
3173+ begin: to.shift (Offset (forwardDirection * topNavBarBox.size.width, 0.0 )),
3174+ end: to,
3175+ );
3176+
3177+ Widget child = topNavBarBottom.child;
3178+
3179+ // Fade in only if this is not a CupertinoSliverNavigationBar.search to
3180+ // CupertinoSliverNavigationBar.search transition.
3181+ if (bottomNavBarBottom == null ||
3182+ bottomNavBarBottom.child is ! _InactiveSearchableBottom ||
3183+ topNavBarBottom.child is ! _InactiveSearchableBottom ) {
3184+ child = FadeTransition (opacity: fadeInFrom (0.0 ), child: child);
3185+ }
3186+
3187+ return PositionedTransition (rect: animation.drive (positionTween), child: child);
3188+ }
31063189}
31073190
31083191/// Navigation bars' hero rect tween that will move between the static bars
0 commit comments