manager: change FAB visibility with scrolling gestures on module screen list

This commit is contained in:
Rifat Azad
2025-06-28 19:45:32 +06:00
parent d80a3ebcda
commit 93dc61e113

View File

@@ -77,6 +77,12 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.core.tween
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
@@ -164,6 +170,30 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
contract = ActivityResultContracts.StartActivityForResult() contract = ActivityResultContracts.StartActivityForResult()
) { viewModel.fetchModuleList() } ) { viewModel.fetchModuleList() }
val listState = rememberLazyListState()
var showFab by remember { mutableStateOf(true) }
LaunchedEffect(listState) {
var lastIndex = listState.firstVisibleItemIndex
var lastOffset = listState.firstVisibleItemScrollOffset
snapshotFlow { listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset }
.collect { (currIndex, currOffset) ->
val isScrollingDown = currIndex > lastIndex ||
(currIndex == lastIndex && currOffset > lastOffset + 4)
val isScrollingUp = currIndex < lastIndex ||
(currIndex == lastIndex && currOffset < lastOffset - 4)
when {
isScrollingDown && showFab -> showFab = false
isScrollingUp && !showFab -> showFab = true
}
lastIndex = currIndex
lastOffset = currOffset
}
}
Scaffold( Scaffold(
topBar = { topBar = {
SearchAppBar( SearchAppBar(
@@ -289,6 +319,17 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
}, },
floatingActionButton = { floatingActionButton = {
if (!hideInstallButton) { if (!hideInstallButton) {
AnimatedVisibility(
visible = showFab,
enter = scaleIn(
animationSpec = tween(200),
initialScale = 0.8f
) + fadeIn(animationSpec = tween(400)),
exit = scaleOut(
animationSpec = tween(200),
targetScale = 0.8f
) + fadeOut(animationSpec = tween(400))
) {
val moduleInstall = stringResource(id = R.string.module_install) val moduleInstall = stringResource(id = R.string.module_install)
val selectZipLauncher = rememberLauncherForActivityResult( val selectZipLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult() contract = ActivityResultContracts.StartActivityForResult()
@@ -330,6 +371,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
text = { Text(text = moduleInstall) }, text = { Text(text = moduleInstall) },
) )
} }
}
}, },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
snackbarHost = { SnackbarHost(hostState = snackBarHost) } snackbarHost = { SnackbarHost(hostState = snackBarHost) }
@@ -370,7 +412,8 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
} }
}, },
context = context, context = context,
snackBarHost = snackBarHost snackBarHost = snackBarHost,
listState = listState
) )
} }
} }
@@ -388,6 +431,7 @@ private fun ModuleList(
onClickModule: (id: String, name: String, hasWebUi: Boolean) -> Unit, onClickModule: (id: String, name: String, hasWebUi: Boolean) -> Unit,
context: Context, context: Context,
snackBarHost: SnackbarHostState, snackBarHost: SnackbarHostState,
listState: LazyListState
) { ) {
val failedEnable = stringResource(R.string.module_failed_to_enable) val failedEnable = stringResource(R.string.module_failed_to_enable)
val failedDisable = stringResource(R.string.module_failed_to_disable) val failedDisable = stringResource(R.string.module_failed_to_disable)
@@ -560,6 +604,7 @@ private fun ModuleList(
isRefreshing = viewModel.isRefreshing isRefreshing = viewModel.isRefreshing
) { ) {
LazyColumn( LazyColumn(
state = listState,
modifier = modifier, modifier = modifier,
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = remember { contentPadding = remember {
@@ -567,7 +612,7 @@ private fun ModuleList(
start = 16.dp, start = 16.dp,
top = 16.dp, top = 16.dp,
end = 16.dp, end = 16.dp,
bottom = 16.dp + 56.dp + 16.dp + 48.dp + 6.dp /* Scaffold Fab Spacing + Fab container height + SnackBar height */ bottom = 16.dp
) )
}, },
) { ) {