From 677846a7488a60a4a58ca158b595288dda5b34f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Orzech?= Date: Thu, 19 Mar 2026 14:10:46 +0100 Subject: [PATCH] feat: add bouncy FAB entrance and press animations --- .../swoosh/microblog/ui/feed/FeedScreen.kt | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/swoosh/microblog/ui/feed/FeedScreen.kt b/app/src/main/java/com/swoosh/microblog/ui/feed/FeedScreen.kt index a3203da..e2142f0 100644 --- a/app/src/main/java/com/swoosh/microblog/ui/feed/FeedScreen.kt +++ b/app/src/main/java/com/swoosh/microblog/ui/feed/FeedScreen.kt @@ -6,8 +6,11 @@ import android.content.Context import android.content.Intent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.foundation.gestures.detectTapGestures +import com.swoosh.microblog.ui.animation.SwooshMotion import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -112,6 +115,23 @@ fun FeedScreen( var showDeleteConfirmation by remember { mutableStateOf(null) } var showRenameDialog by remember { mutableStateOf(null) } + // FAB entrance animation + var fabVisible by remember { mutableStateOf(false) } + val fabScale by animateFloatAsState( + targetValue = if (fabVisible) 1f else 0f, + animationSpec = SwooshMotion.bouncy(), + label = "fabEntrance" + ) + LaunchedEffect(Unit) { fabVisible = true } + + // FAB press animation + var fabPressed by remember { mutableStateOf(false) } + val fabPressScale by animateFloatAsState( + targetValue = if (fabPressed) 0.85f else 1f, + animationSpec = SwooshMotion.bouncyQuick(), + label = "fabPress" + ) + // Pull-to-refresh val pullRefreshState = rememberPullRefreshState( refreshing = state.isRefreshing, @@ -232,7 +252,23 @@ fun FeedScreen( }, floatingActionButton = { if (!isSearchActive) { - FloatingActionButton(onClick = onCompose) { + FloatingActionButton( + onClick = onCompose, + modifier = Modifier + .graphicsLayer { + scaleX = fabScale * fabPressScale + scaleY = fabScale * fabPressScale + } + .pointerInput(Unit) { + detectTapGestures( + onPress = { + fabPressed = true + tryAwaitRelease() + fabPressed = false + } + ) + } + ) { Icon(Icons.Default.Add, contentDescription = "New post") } }