Skip to content

Commit 8e929ba

Browse files
authored
Merge pull request #776 from microsoft/user/joypal/snackbarSwipeToDismiss
[V2 Snackbar] swipe to dismiss implementation
2 parents 673ee9a + 58208b7 commit 8e929ba

File tree

2 files changed

+54
-4
lines changed
  • FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos
  • fluentui_notification/src/main/java/com/microsoft/fluentui/tokenized/notification

2 files changed

+54
-4
lines changed

FluentUI.Demo/src/main/java/com/microsoft/fluentuidemo/demos/V2SnackbarActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ class V2SnackbarActivity : V2DemoActivity() {
320320
)
321321
}
322322
Box(Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
323-
Snackbar(snackbarState, Modifier.padding(bottom = 12.dp))
323+
Snackbar(snackbarState, Modifier.padding(bottom = 12.dp), null, true)
324324
}
325325
}
326326
}

fluentui_notification/src/main/java/com/microsoft/fluentui/tokenized/notification/Snackbar.kt

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.microsoft.fluentui.tokenized.notification
22

3+
import androidx.compose.animation.core.tween
34
import androidx.compose.foundation.background
45
import androidx.compose.foundation.clickable
6+
import androidx.compose.foundation.gestures.detectHorizontalDragGestures
57
import androidx.compose.foundation.interaction.MutableInteractionSource
68
import androidx.compose.foundation.layout.*
79
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -14,6 +16,8 @@ import androidx.compose.ui.Alignment
1416
import androidx.compose.ui.Modifier
1517
import androidx.compose.ui.draw.clip
1618
import androidx.compose.ui.graphics.graphicsLayer
19+
import androidx.compose.ui.input.pointer.pointerInput
20+
import androidx.compose.ui.platform.LocalConfiguration
1721
import androidx.compose.ui.platform.testTag
1822
import androidx.compose.ui.semantics.LiveRegionMode
1923
import androidx.compose.ui.semantics.Role
@@ -27,6 +31,7 @@ import com.microsoft.fluentui.theme.token.Icon
2731
import com.microsoft.fluentui.theme.token.StateColor
2832
import com.microsoft.fluentui.theme.token.controlTokens.*
2933
import com.microsoft.fluentui.tokenized.controls.Button
34+
import com.microsoft.fluentui.util.dpToPx
3035
import kotlinx.coroutines.CancellableContinuation
3136
import kotlinx.coroutines.CoroutineScope
3237
import kotlinx.coroutines.delay
@@ -154,6 +159,40 @@ class SnackbarState {
154159
}
155160
}
156161

162+
@Composable
163+
fun Modifier.swipeToDismiss(
164+
animationVariables: AnimationVariables,
165+
scope: CoroutineScope,
166+
metadata: SnackbarMetadata
167+
): Modifier {
168+
val configuration = LocalConfiguration.current
169+
val dismissThreshold =
170+
dpToPx(configuration.screenWidthDp.dp) * 0.33f // One-third of screen width
171+
return this.pointerInput(Unit) {
172+
detectHorizontalDragGestures(
173+
onDragEnd = {
174+
if (animationVariables.offsetX.value < -dismissThreshold) {
175+
scope.launch {
176+
metadata.dismiss()
177+
}
178+
} else {
179+
scope.launch {
180+
animationVariables.offsetX.animateTo(
181+
0f,
182+
animationSpec = tween(300)
183+
)
184+
}
185+
}
186+
},
187+
onHorizontalDrag = { _, dragAmount ->
188+
scope.launch {
189+
animationVariables.offsetX.snapTo(animationVariables.offsetX.value + dragAmount)
190+
}
191+
}
192+
)
193+
}
194+
}
195+
157196
/**
158197
* Snackbar are transient Notification control used to deliver information which can be timedout or
159198
* can be cleared by user pressing the CTA or dismiss icon. Snackbar is rendered using [SnackbarMetadata]
@@ -163,14 +202,16 @@ class SnackbarState {
163202
* @param snackbarState Queue to store all the Notification requests.
164203
* @param modifier Optional modifier to be applied to Snackbar.
165204
* @param snackbarTokens Optional Tokens to redesign Snackbar.
205+
* @param enableSwipeToDismiss Optional flag to enable swipe to dismiss functionality.
166206
*/
167207
@Composable
168208
fun Snackbar(
169209
snackbarState: SnackbarState,
170210
modifier: Modifier = Modifier,
171-
snackbarTokens: SnackBarTokens? = null
211+
snackbarTokens: SnackBarTokens? = null,
212+
enableSwipeToDismiss: Boolean = false
172213
) {
173-
val metadata: SnackbarMetadata = snackbarState.currentSnackbar ?: return
214+
val metadata = snackbarState.currentSnackbar ?: return
174215
val scope = rememberCoroutineScope()
175216

176217
val themeID =
@@ -195,8 +236,17 @@ fun Snackbar(
195236
scope = scope,
196237
animationBehavior = metadata.animationBehavior,
197238
) { animationVariables ->
198-
Row(
239+
val swipeToDismissModifier = if (enableSwipeToDismiss) {
240+
modifier.swipeToDismiss(
241+
animationVariables,
242+
scope,
243+
metadata
244+
)
245+
} else {
199246
modifier
247+
}
248+
Row(
249+
swipeToDismissModifier
200250
.graphicsLayer(
201251
scaleX = animationVariables.scale.value,
202252
scaleY = animationVariables.scale.value,

0 commit comments

Comments
 (0)