androidandroid-layoutandroid-jetpack-composeandroid-jetpack

How to do this Scroll hide fab button in Jetpack Compose with transaction


How to do this Scroll hide fab button in Jetpack Compose with transaction

Like this I need it:


Solution

  • You can use NestedScrollConnection and AnimatedVisibility to change visibilty of ExtendedFloatingActionButton.

    1. You should use remeberSaveable to store state of ExtendedFloatingActionButton:
    // Visibility for FAB, could be saved in viewModel
    val isVisible = rememberSaveable { mutableStateOf(true) }
    
    1. Implement NestedScrollConnection, you can use your values to show/hide FAB:
    // Nested scroll for control FAB
    val nestedScrollConnection = remember {
        object : NestedScrollConnection {
            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
                // Hide FAB
                if (available.y < -1) {
                    isVisible.value = false
                }
    
                // Show FAB
                if (available.y > 1) {
                    isVisible.value = true
                }
    
                return Offset.Zero
            }
        }
    }
    
    1. Implement ExtendedFloatingActionButton inside AnimatedVisibility, set visible value from isVisible and set enter and exit animation, in my case I use slideInVertically for enter animation and slideOutVertically for exit animation:
    AnimatedVisibility(
        visible = isVisible.value,
        enter = slideInVertically(initialOffsetY = { it * 2 }),
        exit = slideOutVertically(targetOffsetY = { it * 2 }),
    ) {
        ExtendedFloatingActionButton(
            onClick = {
                // FAB click
            }
        ) {
            Text(
                text = "Extended FAB"
            )
        }
    }
    
    1. Apply NestedScrollConnection to scrollable content, in my case to LazyColumn:
    LazyColumn(
        modifier = Modifier
            .fillMaxWidth()
            .padding(innerPadding)
            .nestedScroll(nestedScrollConnection),
    ) {
        items(100) { index ->
            Text(
                modifier = Modifier.padding(16.dp),
                text = "Item: $index"
            )
        }
    }
    

    Full code of MainActivity:

    package com.andreirozov.animatedfab
    
    import android.os.Bundle
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.compose.animation.AnimatedVisibility
    import androidx.compose.animation.slideInHorizontally
    import androidx.compose.animation.slideInVertically
    import androidx.compose.animation.slideOutHorizontally
    import androidx.compose.animation.slideOutVertically
    import androidx.compose.foundation.background
    import androidx.compose.foundation.layout.fillMaxWidth
    import androidx.compose.foundation.layout.padding
    import androidx.compose.foundation.lazy.LazyColumn
    import androidx.compose.material.icons.Icons
    import androidx.compose.material.icons.rounded.Add
    import androidx.compose.material3.ExperimentalMaterial3Api
    import androidx.compose.material3.ExtendedFloatingActionButton
    import androidx.compose.material3.FabPosition
    import androidx.compose.material3.FloatingActionButton
    import androidx.compose.material3.Icon
    import androidx.compose.material3.Scaffold
    import androidx.compose.material3.Text
    import androidx.compose.runtime.Composable
    import androidx.compose.runtime.mutableStateOf
    import androidx.compose.runtime.remember
    import androidx.compose.runtime.saveable.rememberSaveable
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.geometry.Offset
    import androidx.compose.ui.graphics.Color
    import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
    import androidx.compose.ui.input.nestedscroll.NestedScrollSource
    import androidx.compose.ui.input.nestedscroll.nestedScroll
    import androidx.compose.ui.unit.dp
    import com.andreirozov.animatedfab.ui.theme.AnimatedFabTheme
    
    class MainActivity : ComponentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContent {
                AnimatedFabApp()
            }
        }
    }
    
    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    fun AnimatedFabApp() {
        AnimatedFabTheme {
            // Visibility for FAB, could be saved in viewModel
            val isVisible = rememberSaveable { mutableStateOf(true) }
    
            // Nested scroll for control FAB
            val nestedScrollConnection = remember {
                object : NestedScrollConnection {
                    override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
                        // Hide FAB
                        if (available.y < -1) {
                            isVisible.value = false
                        }
    
                        // Show FAB
                        if (available.y > 1) {
                            isVisible.value = true
                        }
    
                        return Offset.Zero
                    }
                }
            }
    
            Scaffold(
                floatingActionButtonPosition = FabPosition.Center,
                floatingActionButton = {
                    AnimatedVisibility(
                        visible = isVisible.value,
                        enter = slideInVertically(initialOffsetY = { it * 2 }),
                        exit = slideOutVertically(targetOffsetY = { it * 2 }),
                    ) {
                        ExtendedFloatingActionButton(
                            onClick = {
                                // FAB click
                            }
                        ) {
                            Text(
                                text = "Extended FAB"
                            )
                        }
                    }
                }
            ) { innerPadding ->
                LazyColumn(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(innerPadding)
                        .nestedScroll(nestedScrollConnection),
                ) {
                    items(100) { index ->
                        Text(
                            modifier = Modifier.padding(16.dp),
                            text = "Item: $index"
                        )
                    }
                }
            }
        }
    }
    

    Result:

    enter image description here