Skip to content

Commit dc2049b

Browse files
Merge pull request #111 from syncfusion/BottomSheetBugFixes
Improve animation for the Maui Bottom sheet control
2 parents 0bbd7df + 4373e62 commit dc2049b

File tree

3 files changed

+158
-55
lines changed

3 files changed

+158
-55
lines changed

maui/src/BottomSheet/BottomSheetBorder.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ internal class BottomSheetBorder : SfBorder, ITouchListener
1818
// To store the child count of bottom sheet
1919
double _childLoopCount;
2020

21+
// To handle bottom sheet swipe based on initial touch point.
22+
bool _canHandleTouch = true;
23+
2124
#endif
2225

23-
#endregion
26+
#endregion
2427

2528
#region Constructor
2629

@@ -123,10 +126,21 @@ public void OnTouch(Toolkit.Internals.PointerEventArgs e)
123126
}
124127

125128
var firstDescendant = Content.GetVisualTreeDescendants().FirstOrDefault();
126-
if (firstDescendant is not null && IsChildElementScrolled(firstDescendant, e.TouchPoint))
129+
if (firstDescendant is not null && _canHandleTouch && IsChildElementScrolled(firstDescendant, e.TouchPoint))
127130
{
128131
return;
129132
}
133+
else
134+
{
135+
if (e is not null && e.Action == PointerActions.Pressed)
136+
{
137+
_canHandleTouch = false;
138+
}
139+
else if (e is not null && e.Action == PointerActions.Released)
140+
{
141+
_canHandleTouch = true;
142+
}
143+
}
130144

131145
#endif
132146
if (e is not null && _bottomSheetRef is not null)

maui/src/BottomSheet/SfBottomSheet.cs

Lines changed: 126 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public partial class SfBottomSheet : SfView, IParentThemeElement
4444
/// <summary>
4545
/// The shape used to provide corner radius for the grabber.
4646
/// </summary>
47-
RoundRectangle? _grabberStrokeShape;
47+
RoundRectangle? _grabberStrokeShape;
4848

4949
// Shape
5050
/// <summary>
@@ -823,7 +823,6 @@ public void Show()
823823
}
824824

825825
SetupBottomSheetForShow();
826-
_isSheetOpen = true;
827826
AnimateBottomSheet(GetTargetPosition());
828827
IsOpen = true;
829828
}
@@ -1028,7 +1027,8 @@ void InitializeBottomSheetBorder()
10281027
HeightRequest = CalculateInitialHeight(),
10291028
IsVisible = false,
10301029
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
10321032
};
10331033
}
10341034

@@ -1048,8 +1048,7 @@ void InitializeContentBorder()
10481048
{
10491049
_contentBorder = new SfBorder()
10501050
{
1051-
StrokeThickness = 0,
1052-
Padding = ContentPadding
1051+
StrokeThickness = 0
10531052
};
10541053
}
10551054

@@ -1298,9 +1297,9 @@ CornerRadius EnsureValidCornerRadius(CornerRadius cornerRadius)
12981297
/// <param name="padding">The new padding to be applied.</param>
12991298
void UpdatePadding(Thickness padding)
13001299
{
1301-
if (_contentBorder is not null && !_contentBorder.Padding.Equals(padding))
1300+
if (_bottomSheet is not null && !_bottomSheet.Padding.Equals(padding))
13021301
{
1303-
_contentBorder.Padding = padding;
1302+
_bottomSheet.Padding = padding;
13041303
OnPropertyChanged(nameof(ContentPadding));
13051304
}
13061305
}
@@ -1316,7 +1315,7 @@ void UpdateGrabberHeightProperty(double newValue)
13161315
return;
13171316
}
13181317

1319-
_grabber.HeightRequest = (newValue<0) ? (double)(GrabberHeightProperty.DefaultValue) : newValue;
1318+
_grabber.HeightRequest = (newValue<=0) ? (double)(GrabberHeightProperty.DefaultValue) : newValue;
13201319
}
13211320

13221321
/// <summary>
@@ -1330,7 +1329,7 @@ void UpdateGrabberWidthProperty(double newValue)
13301329
return;
13311330
}
13321331

1333-
_grabber.WidthRequest = (newValue<0) ? (double)GrabberWidthProperty.DefaultValue : newValue;
1332+
_grabber.WidthRequest = (newValue<=0) ? (double)GrabberWidthProperty.DefaultValue : newValue;
13341333
}
13351334

13361335

@@ -1495,15 +1494,29 @@ void UpdateStateBasedOnNearestPoint()
14951494

14961495
if (nearestPoint == fullExpandedHeight)
14971496
{
1498-
State = BottomSheetState.FullExpanded;
1497+
if(State is not BottomSheetState.FullExpanded)
1498+
{
1499+
State = BottomSheetState.FullExpanded;
1500+
}
1501+
else
1502+
{
1503+
Show();
1504+
}
14991505
}
15001506
else if (nearestPoint == halfExpandedHeight)
15011507
{
15021508
State = BottomSheetState.HalfExpanded;
15031509
}
15041510
else
15051511
{
1506-
State = BottomSheetState.Collapsed;
1512+
if (State is not BottomSheetState.Collapsed)
1513+
{
1514+
State = BottomSheetState.Collapsed;
1515+
}
1516+
else
1517+
{
1518+
Show();
1519+
}
15071520
}
15081521
}
15091522

@@ -1519,12 +1532,7 @@ void UpdateStateChanged(BottomSheetState oldState, BottomSheetState newState)
15191532
{
15201533
_stateChangedEventArgs.OldState = oldState;
15211534
_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);
15281536
}
15291537
}
15301538

@@ -1611,12 +1619,7 @@ double GetFullExpandedPosition()
16111619
double GetCollapsedPosition()
16121620
{
16131621
double targetPosition = Height - CollapsedHeight;
1614-
if (_overlayGrid is not null)
1615-
{
1616-
_overlayGrid.IsVisible = false;
1617-
}
1618-
1619-
return targetPosition;
1622+
return targetPosition;
16201623
}
16211624

16221625
/// <summary>
@@ -1645,23 +1648,68 @@ double GetHalfExpandedPosition()
16451648
/// <param name="onFinish">Optional action to be executed when the animation finishes.</param>
16461649
void AnimateBottomSheet(double targetPosition, Action? onFinish = null)
16471650
{
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+
}
16501660

1661+
const int animationDuration = 150;
1662+
const int topPadding = 2;
1663+
_isSheetOpen = true;
16511664
if (_bottomSheet is not null)
16521665
{
16531666
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) =>
16551668
{
16561669
UpdateBottomSheetHeight();
16571670
onFinish?.Invoke();
16581671
});
16591672
}
16601673

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+
{
16611682
if (_overlayGrid is not null)
16621683
{
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+
}
16651713
}
16661714
}
16671715

@@ -1767,14 +1815,31 @@ bool ShouldRestrictMovement(double newTranslationY, double diffY)
17671815
return false;
17681816
}
17691817

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 &&
17711836
AllowedState is BottomSheetAllowedState.HalfExpanded &&
1772-
_bottomSheet.TranslationY > newTranslationY;
1837+
updatedHeight > endPosition;
17731838

1774-
bool isCollapsedAndMovingDown = State is BottomSheetState.Collapsed && diffY > 0;
1839+
bool isCollapsedAndMovingDown = State is BottomSheetState.Collapsed && updatedHeight < endPosition;
17751840

17761841
bool isFullExpandedRestricted = State is BottomSheetState.FullExpanded &&
1777-
_bottomSheet.TranslationY > newTranslationY;
1842+
updatedHeight > endPosition;
17781843

17791844
bool isBehind = (newTranslationY > Height - CollapsedHeight) || (newTranslationY < Height * (1 - FullExpandedRatio));
17801845

@@ -1789,14 +1854,35 @@ AllowedState is BottomSheetAllowedState.HalfExpanded &&
17891854
/// <param name="touchY">The current Y coordinate of the touch point.</param>
17901855
void UpdateBottomSheetPosition(double newTranslationY, double touchY)
17911856
{
1792-
if (_bottomSheet is null)
1857+
if (_bottomSheet is null || _overlayGrid is null)
17931858
{
17941859
return;
17951860
}
17961861

17971862
_bottomSheet.TranslationY = newTranslationY;
17981863
_initialTouchY = touchY;
17991864
_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;
18001886
}
18011887

18021888
/// <summary>
@@ -1809,7 +1895,10 @@ void HandleTouchReleased(double touchY)
18091895
_initialTouchY = 0;
18101896
_isPointerPressed = false;
18111897

1812-
UpdatePosition();
1898+
if(_bottomSheet is not null && touchY >= _bottomSheet.TranslationY)
1899+
{
1900+
UpdatePosition();
1901+
}
18131902
}
18141903

18151904

@@ -1940,9 +2029,10 @@ static void OnIsModalPropertyChanged(BindableObject bindable, object oldValue, o
19402029
{
19412030
if (bindable is SfBottomSheet sheet)
19422031
{
1943-
if (sheet._overlayGrid is not null)
2032+
if (sheet._overlayGrid is not null && (sheet.State is BottomSheetState.FullExpanded || sheet.State is BottomSheetState.HalfExpanded))
19442033
{
19452034
sheet._overlayGrid.IsVisible = sheet.IsModal;
2035+
sheet.AnimateOverlay(150);
19462036
}
19472037
}
19482038
}
@@ -2035,7 +2125,7 @@ static void OnStatePropertyChanged(BindableObject bindable, object oldValue, obj
20352125

20362126
if (newState == BottomSheetState.Hidden)
20372127
{
2038-
sheet._isHalfExpanded = true;
2128+
sheet._isHalfExpanded = (sheet.AllowedState != BottomSheetAllowedState.FullExpanded);
20392129
if (sheet._isSheetOpen)
20402130
{
20412131
sheet._isSheetOpen = false;

0 commit comments

Comments
 (0)