@@ -44,7 +44,7 @@ public partial class SfBottomSheet : SfView, IParentThemeElement
44
44
/// <summary>
45
45
/// The shape used to provide corner radius for the grabber.
46
46
/// </summary>
47
- RoundRectangle ? _grabberStrokeShape ;
47
+ RoundRectangle ? _grabberStrokeShape ;
48
48
49
49
// Shape
50
50
/// <summary>
@@ -823,7 +823,6 @@ public void Show()
823
823
}
824
824
825
825
SetupBottomSheetForShow ( ) ;
826
- _isSheetOpen = true ;
827
826
AnimateBottomSheet ( GetTargetPosition ( ) ) ;
828
827
IsOpen = true ;
829
828
}
@@ -1028,7 +1027,8 @@ void InitializeBottomSheetBorder()
1028
1027
HeightRequest = CalculateInitialHeight ( ) ,
1029
1028
IsVisible = false ,
1030
1029
StrokeShape = _bottomSheetStrokeShape ,
1031
- Content = _bottomSheetContent ?? throw new InvalidOperationException ( "Bottom sheet content is not initialized." )
1030
+ Content = _bottomSheetContent ?? throw new InvalidOperationException ( "Bottom sheet content is not initialized." ) ,
1031
+ Padding = ContentPadding
1032
1032
} ;
1033
1033
}
1034
1034
@@ -1048,8 +1048,7 @@ void InitializeContentBorder()
1048
1048
{
1049
1049
_contentBorder = new SfBorder ( )
1050
1050
{
1051
- StrokeThickness = 0 ,
1052
- Padding = ContentPadding
1051
+ StrokeThickness = 0
1053
1052
} ;
1054
1053
}
1055
1054
@@ -1298,9 +1297,9 @@ CornerRadius EnsureValidCornerRadius(CornerRadius cornerRadius)
1298
1297
/// <param name="padding">The new padding to be applied.</param>
1299
1298
void UpdatePadding ( Thickness padding )
1300
1299
{
1301
- if ( _contentBorder is not null && ! _contentBorder . Padding . Equals ( padding ) )
1300
+ if ( _bottomSheet is not null && ! _bottomSheet . Padding . Equals ( padding ) )
1302
1301
{
1303
- _contentBorder . Padding = padding ;
1302
+ _bottomSheet . Padding = padding ;
1304
1303
OnPropertyChanged ( nameof ( ContentPadding ) ) ;
1305
1304
}
1306
1305
}
@@ -1316,7 +1315,7 @@ void UpdateGrabberHeightProperty(double newValue)
1316
1315
return ;
1317
1316
}
1318
1317
1319
- _grabber . HeightRequest = ( newValue < 0 ) ? ( double ) ( GrabberHeightProperty . DefaultValue ) : newValue ;
1318
+ _grabber . HeightRequest = ( newValue <= 0 ) ? ( double ) ( GrabberHeightProperty . DefaultValue ) : newValue ;
1320
1319
}
1321
1320
1322
1321
/// <summary>
@@ -1330,7 +1329,7 @@ void UpdateGrabberWidthProperty(double newValue)
1330
1329
return ;
1331
1330
}
1332
1331
1333
- _grabber . WidthRequest = ( newValue < 0 ) ? ( double ) GrabberWidthProperty . DefaultValue : newValue ;
1332
+ _grabber . WidthRequest = ( newValue <= 0 ) ? ( double ) GrabberWidthProperty . DefaultValue : newValue ;
1334
1333
}
1335
1334
1336
1335
@@ -1495,15 +1494,29 @@ void UpdateStateBasedOnNearestPoint()
1495
1494
1496
1495
if ( nearestPoint == fullExpandedHeight )
1497
1496
{
1498
- State = BottomSheetState . FullExpanded ;
1497
+ if ( State is not BottomSheetState . FullExpanded )
1498
+ {
1499
+ State = BottomSheetState . FullExpanded ;
1500
+ }
1501
+ else
1502
+ {
1503
+ Show ( ) ;
1504
+ }
1499
1505
}
1500
1506
else if ( nearestPoint == halfExpandedHeight )
1501
1507
{
1502
1508
State = BottomSheetState . HalfExpanded ;
1503
1509
}
1504
1510
else
1505
1511
{
1506
- State = BottomSheetState . Collapsed ;
1512
+ if ( State is not BottomSheetState . Collapsed )
1513
+ {
1514
+ State = BottomSheetState . Collapsed ;
1515
+ }
1516
+ else
1517
+ {
1518
+ Show ( ) ;
1519
+ }
1507
1520
}
1508
1521
}
1509
1522
@@ -1519,12 +1532,7 @@ void UpdateStateChanged(BottomSheetState oldState, BottomSheetState newState)
1519
1532
{
1520
1533
_stateChangedEventArgs . OldState = oldState ;
1521
1534
_stateChangedEventArgs . NewState = newState ;
1522
- if ( _overlayGrid is not null )
1523
- {
1524
- _overlayGrid . IsVisible = ( State is BottomSheetState . Collapsed ) ? false : IsModal ;
1525
- }
1526
-
1527
- OnStateChanged ( _stateChangedEventArgs ) ;
1535
+ OnStateChanged ( _stateChangedEventArgs ) ;
1528
1536
}
1529
1537
}
1530
1538
@@ -1611,12 +1619,7 @@ double GetFullExpandedPosition()
1611
1619
double GetCollapsedPosition ( )
1612
1620
{
1613
1621
double targetPosition = Height - CollapsedHeight ;
1614
- if ( _overlayGrid is not null )
1615
- {
1616
- _overlayGrid . IsVisible = false ;
1617
- }
1618
-
1619
- return targetPosition ;
1622
+ return targetPosition ;
1620
1623
}
1621
1624
1622
1625
/// <summary>
@@ -1645,23 +1648,68 @@ double GetHalfExpandedPosition()
1645
1648
/// <param name="onFinish">Optional action to be executed when the animation finishes.</param>
1646
1649
void AnimateBottomSheet ( double targetPosition , Action ? onFinish = null )
1647
1650
{
1648
- const int AnimationDuration = 150 ;
1649
- const int topPadding = 2 ;
1651
+ if ( _bottomSheet . AnimationIsRunning ( "bottomSheetAnimation" ) )
1652
+ {
1653
+ _bottomSheet . AbortAnimation ( "bottomSheetAnimation" ) ;
1654
+ }
1655
+
1656
+ if ( _overlayGrid . AnimationIsRunning ( "overlayGridAnimation" ) )
1657
+ {
1658
+ _overlayGrid . AbortAnimation ( "overlayGridAnimation" ) ;
1659
+ }
1650
1660
1661
+ const int animationDuration = 150 ;
1662
+ const int topPadding = 2 ;
1663
+ _isSheetOpen = true ;
1651
1664
if ( _bottomSheet is not null )
1652
1665
{
1653
1666
var bottomSheetAnimation = new Animation ( d => _bottomSheet . TranslationY = d , _bottomSheet . TranslationY , targetPosition + topPadding ) ;
1654
- _bottomSheet ? . Animate ( "bottomSheetAnimation" , bottomSheetAnimation , length : AnimationDuration , easing : Easing . Linear , finished : ( v , e ) =>
1667
+ _bottomSheet ? . Animate ( "bottomSheetAnimation" , bottomSheetAnimation , length : animationDuration , easing : Easing . Linear , finished : ( v , e ) =>
1655
1668
{
1656
1669
UpdateBottomSheetHeight ( ) ;
1657
1670
onFinish ? . Invoke ( ) ;
1658
1671
} ) ;
1659
1672
}
1660
1673
1674
+ AnimateOverlay ( animationDuration ) ;
1675
+ }
1676
+
1677
+ /// <summary>
1678
+ /// Animates the overlay of the bottom sheet based on state transitions.
1679
+ /// </summary>
1680
+ void AnimateOverlay ( int animationDuration )
1681
+ {
1661
1682
if ( _overlayGrid is not null )
1662
1683
{
1663
- var overlayGridAnimation = new Animation ( d => _overlayGrid . Opacity = d , _overlayGrid . Opacity , _isSheetOpen ? DefaultOverlayOpacity : 0 ) ;
1664
- _overlayGrid ? . Animate ( "overlayGridAnimation" , overlayGridAnimation , length : AnimationDuration , easing : Easing . Linear ) ;
1684
+ double startValue = 0 ;
1685
+ double endValue = 0 ;
1686
+ _overlayGrid . IsVisible = IsModal ;
1687
+
1688
+ if ( IsModal )
1689
+ {
1690
+ if ( State is BottomSheetState . Collapsed || State is BottomSheetState . Hidden )
1691
+ {
1692
+ startValue = _overlayGrid . Opacity ;
1693
+ endValue = 0 ;
1694
+ }
1695
+ else
1696
+ {
1697
+ startValue = _overlayGrid . Opacity ;
1698
+ endValue = DefaultOverlayOpacity ;
1699
+ }
1700
+
1701
+ var overlayGridAnimation = new Animation ( d => _overlayGrid . Opacity = d , startValue , endValue ) ;
1702
+ _overlayGrid . Animate ( "overlayGridAnimation" , overlayGridAnimation ,
1703
+ length : ( uint ) animationDuration ,
1704
+ easing : Easing . Linear ,
1705
+ finished : ( e , v ) =>
1706
+ {
1707
+ if ( State is BottomSheetState . Collapsed || State is BottomSheetState . Hidden )
1708
+ {
1709
+ _overlayGrid . IsVisible = false ;
1710
+ }
1711
+ } ) ;
1712
+ }
1665
1713
}
1666
1714
}
1667
1715
@@ -1767,14 +1815,31 @@ bool ShouldRestrictMovement(double newTranslationY, double diffY)
1767
1815
return false ;
1768
1816
}
1769
1817
1770
- bool isHalfExpandedAndRestricted = State is BottomSheetState . HalfExpanded &&
1818
+ double endPosition = 0 ;
1819
+ double updatedHeight = Height - newTranslationY ;
1820
+ switch ( State )
1821
+ {
1822
+ case BottomSheetState . FullExpanded :
1823
+ endPosition = Height * FullExpandedRatio ;
1824
+ break ;
1825
+
1826
+ case BottomSheetState . HalfExpanded :
1827
+ endPosition = Height * HalfExpandedRatio ;
1828
+ break ;
1829
+
1830
+ case BottomSheetState . Collapsed :
1831
+ endPosition = CollapsedHeight ;
1832
+ break ;
1833
+ }
1834
+
1835
+ bool isHalfExpandedAndRestricted = State is BottomSheetState . HalfExpanded &&
1771
1836
AllowedState is BottomSheetAllowedState . HalfExpanded &&
1772
- _bottomSheet . TranslationY > newTranslationY ;
1837
+ updatedHeight > endPosition ;
1773
1838
1774
- bool isCollapsedAndMovingDown = State is BottomSheetState . Collapsed && diffY > 0 ;
1839
+ bool isCollapsedAndMovingDown = State is BottomSheetState . Collapsed && updatedHeight < endPosition ;
1775
1840
1776
1841
bool isFullExpandedRestricted = State is BottomSheetState . FullExpanded &&
1777
- _bottomSheet . TranslationY > newTranslationY ;
1842
+ updatedHeight > endPosition ;
1778
1843
1779
1844
bool isBehind = ( newTranslationY > Height - CollapsedHeight ) || ( newTranslationY < Height * ( 1 - FullExpandedRatio ) ) ;
1780
1845
@@ -1789,14 +1854,35 @@ AllowedState is BottomSheetAllowedState.HalfExpanded &&
1789
1854
/// <param name="touchY">The current Y coordinate of the touch point.</param>
1790
1855
void UpdateBottomSheetPosition ( double newTranslationY , double touchY )
1791
1856
{
1792
- if ( _bottomSheet is null )
1857
+ if ( _bottomSheet is null || _overlayGrid is null )
1793
1858
{
1794
1859
return ;
1795
1860
}
1796
1861
1797
1862
_bottomSheet . TranslationY = newTranslationY ;
1798
1863
_initialTouchY = touchY ;
1799
1864
_bottomSheet . HeightRequest = Height - newTranslationY ;
1865
+ _overlayGrid . IsVisible = IsModal && ( _bottomSheet . HeightRequest > CollapsedHeight ) ;
1866
+ _overlayGrid . Opacity = CalculateOverlayOpacity ( _bottomSheet . HeightRequest ) ;
1867
+ }
1868
+
1869
+ /// <summary>
1870
+ /// Calculates the overlay opacity based on the current height of the bottom sheet.
1871
+ /// </summary>
1872
+ /// <param name="currentHeight">The current height of the bottom sheet.</param>
1873
+ /// <returns>The calculated opacity value ranging from 0 to 0.5</returns>
1874
+ double CalculateOverlayOpacity ( double currentHeight )
1875
+ {
1876
+ const double maxOpacity = 0.5 ;
1877
+
1878
+ // Calculate how far along the transition from collapsed to half-expanded.
1879
+ double transitionProgress = ( currentHeight - CollapsedHeight ) / ( ( HalfExpandedRatio * Height ) - CollapsedHeight ) ;
1880
+
1881
+ // Clamp the transition progress to between 0 and 1.
1882
+ transitionProgress = Math . Clamp ( transitionProgress , 0 , 1 ) ;
1883
+
1884
+ // Calculate and return the opacity based on the transition progress.
1885
+ return transitionProgress * maxOpacity ;
1800
1886
}
1801
1887
1802
1888
/// <summary>
@@ -1809,7 +1895,10 @@ void HandleTouchReleased(double touchY)
1809
1895
_initialTouchY = 0 ;
1810
1896
_isPointerPressed = false ;
1811
1897
1812
- UpdatePosition ( ) ;
1898
+ if ( _bottomSheet is not null && touchY >= _bottomSheet . TranslationY )
1899
+ {
1900
+ UpdatePosition ( ) ;
1901
+ }
1813
1902
}
1814
1903
1815
1904
@@ -1940,9 +2029,10 @@ static void OnIsModalPropertyChanged(BindableObject bindable, object oldValue, o
1940
2029
{
1941
2030
if ( bindable is SfBottomSheet sheet )
1942
2031
{
1943
- if ( sheet . _overlayGrid is not null )
2032
+ if ( sheet . _overlayGrid is not null && ( sheet . State is BottomSheetState . FullExpanded || sheet . State is BottomSheetState . HalfExpanded ) )
1944
2033
{
1945
2034
sheet . _overlayGrid . IsVisible = sheet . IsModal ;
2035
+ sheet . AnimateOverlay ( 150 ) ;
1946
2036
}
1947
2037
}
1948
2038
}
@@ -2035,7 +2125,7 @@ static void OnStatePropertyChanged(BindableObject bindable, object oldValue, obj
2035
2125
2036
2126
if ( newState == BottomSheetState . Hidden )
2037
2127
{
2038
- sheet . _isHalfExpanded = true ;
2128
+ sheet . _isHalfExpanded = ( sheet . AllowedState != BottomSheetAllowedState . FullExpanded ) ;
2039
2129
if ( sheet . _isSheetOpen )
2040
2130
{
2041
2131
sheet . _isSheetOpen = false ;
0 commit comments