@@ -33,6 +33,8 @@ const double _kNavBarShowLargeTitleThreshold = 10.0;
33
33
34
34
const double _kNavBarEdgePadding = 16.0 ;
35
35
36
+ const double _kNavBarBottomPadding = 8.0 ;
37
+
36
38
const double _kNavBarBackButtonTapWidth = 50.0 ;
37
39
38
40
/// Title text transfer fade.
@@ -833,31 +835,27 @@ class _LargeTitleNavigationBarSliverDelegate
833
835
right: 0.0 ,
834
836
bottom: 0.0 ,
835
837
child: ClipRect (
836
- // The large title starts at the persistent bar.
837
- // It's aligned with the bottom of the sliver and expands clipped
838
- // and behind the persistent bar.
839
- child: OverflowBox (
840
- minHeight: 0.0 ,
841
- maxHeight: double .infinity,
842
- alignment: AlignmentDirectional .bottomStart,
843
- child: Padding (
844
- padding: const EdgeInsetsDirectional .only (
845
- start: _kNavBarEdgePadding,
846
- bottom: 8.0 , // Bottom has a different padding.
847
- ),
848
- child: SafeArea (
849
- top: false ,
850
- bottom: false ,
851
- child: AnimatedOpacity (
852
- opacity: showLargeTitle ? 1.0 : 0.0 ,
853
- duration: _kNavBarTitleFadeDuration,
854
- child: Semantics (
855
- header: true ,
856
- child: DefaultTextStyle (
857
- style: CupertinoTheme .of (context).textTheme.navLargeTitleTextStyle,
858
- maxLines: 1 ,
859
- overflow: TextOverflow .ellipsis,
860
- child: components.largeTitle! ,
838
+ child: Padding (
839
+ padding: const EdgeInsetsDirectional .only (
840
+ start: _kNavBarEdgePadding,
841
+ bottom: _kNavBarBottomPadding
842
+ ),
843
+ child: SafeArea (
844
+ top: false ,
845
+ bottom: false ,
846
+ child: AnimatedOpacity (
847
+ opacity: showLargeTitle ? 1.0 : 0.0 ,
848
+ duration: _kNavBarTitleFadeDuration,
849
+ child: Semantics (
850
+ header: true ,
851
+ child: DefaultTextStyle (
852
+ style: CupertinoTheme .of (context)
853
+ .textTheme
854
+ .navLargeTitleTextStyle,
855
+ maxLines: 1 ,
856
+ overflow: TextOverflow .ellipsis,
857
+ child: _LargeTitle (
858
+ child: components.largeTitle,
861
859
),
862
860
),
863
861
),
@@ -921,6 +919,123 @@ class _LargeTitleNavigationBarSliverDelegate
921
919
}
922
920
}
923
921
922
+ /// The large title of the navigation bar.
923
+ ///
924
+ /// Magnifies on over-scroll when [CupertinoSliverNavigationBar.stretch]
925
+ /// parameter is true.
926
+ class _LargeTitle extends SingleChildRenderObjectWidget {
927
+ const _LargeTitle ({ super .child });
928
+
929
+ @override
930
+ _RenderLargeTitle createRenderObject (BuildContext context) {
931
+ return _RenderLargeTitle (alignment: AlignmentDirectional .bottomStart.resolve (Directionality .of (context)));
932
+ }
933
+
934
+ @override
935
+ void updateRenderObject (BuildContext context, _RenderLargeTitle renderObject) {
936
+ renderObject.alignment = AlignmentDirectional .bottomStart.resolve (Directionality .of (context));
937
+ }
938
+ }
939
+
940
+ class _RenderLargeTitle extends RenderShiftedBox {
941
+ _RenderLargeTitle ({
942
+ required Alignment alignment,
943
+ }) : _alignment = alignment,
944
+ super (null );
945
+
946
+ Alignment get alignment => _alignment;
947
+ Alignment _alignment;
948
+ set alignment (Alignment value) {
949
+ if (_alignment == value) {
950
+ return ;
951
+ }
952
+ _alignment = value;
953
+
954
+ markNeedsLayout ();
955
+ }
956
+
957
+ double _scale = 1.0 ;
958
+
959
+ @override
960
+ void performLayout () {
961
+ final RenderBox ? child = this .child;
962
+ Size childSize = Size .zero;
963
+
964
+ size = constraints.biggest;
965
+
966
+ if (child == null ) {
967
+ return ;
968
+ }
969
+
970
+ final BoxConstraints childConstriants = constraints.widthConstraints ().loosen ();
971
+ child.layout (childConstriants, parentUsesSize: true );
972
+
973
+ final double maxScale = child.size.width != 0.0
974
+ ? clampDouble (constraints.maxWidth / child.size.width, 1.0 , 1.1 )
975
+ : 1.1 ;
976
+ _scale = clampDouble (
977
+ 1.0 + (constraints.maxHeight - (_kNavBarLargeTitleHeightExtension - _kNavBarBottomPadding)) / (_kNavBarLargeTitleHeightExtension - _kNavBarBottomPadding) * 0.03 ,
978
+ 1.0 ,
979
+ maxScale,
980
+ );
981
+
982
+ childSize = child.size * _scale;
983
+ final BoxParentData childParentData = child.parentData! as BoxParentData ;
984
+ childParentData.offset = alignment.alongOffset (size - childSize as Offset );
985
+ }
986
+
987
+ @override
988
+ void applyPaintTransform (RenderBox child, Matrix4 transform) {
989
+ assert (child == this .child);
990
+
991
+ super .applyPaintTransform (child, transform);
992
+
993
+ transform.scale (_scale, _scale);
994
+ }
995
+
996
+ @override
997
+ void paint (PaintingContext context, Offset offset) {
998
+ final RenderBox ? child = this .child;
999
+
1000
+ if (child == null ) {
1001
+ layer = null ;
1002
+ } else {
1003
+ final BoxParentData childParentData = child.parentData! as BoxParentData ;
1004
+
1005
+ layer = context.pushTransform (
1006
+ needsCompositing,
1007
+ offset + childParentData.offset,
1008
+ Matrix4 .diagonal3Values (_scale, _scale, 1.0 ),
1009
+ (PaintingContext context, Offset offset) => context.paintChild (child, offset),
1010
+ oldLayer: layer as TransformLayer ? ,
1011
+ );
1012
+ }
1013
+ }
1014
+
1015
+ @override
1016
+ bool hitTestChildren (BoxHitTestResult result, {required Offset position}) {
1017
+ final RenderBox ? child = this .child;
1018
+
1019
+ if (child == null ) {
1020
+ return false ;
1021
+ }
1022
+
1023
+ final Offset childOffset = (child.parentData! as BoxParentData ).offset;
1024
+
1025
+ final Matrix4 transform = Matrix4 .identity ()
1026
+ ..scale (1.0 / _scale, 1.0 / _scale, 1.0 )
1027
+ ..translate (- childOffset.dx, - childOffset.dy);
1028
+
1029
+ return result.addWithRawTransform (
1030
+ transform: transform,
1031
+ position: position,
1032
+ hitTest: (BoxHitTestResult result, Offset transformed) {
1033
+ return child.hitTest (result, position: transformed);
1034
+ }
1035
+ );
1036
+ }
1037
+ }
1038
+
924
1039
/// The top part of the navigation bar that's never scrolled away.
925
1040
///
926
1041
/// Consists of the entire navigation bar without background and border when used
0 commit comments