https://kotlinlang.org logo
#compose
Title
# compose
u

ursus

03/23/2024, 9:19 PM
Copy code
Snippet
I want to show a bottom sheet with material3, works for the most part. However, I need to hoist my state all the way to
ViewModel
because.. reasons. But now when viewModel sets the state to
false
, then modal hides instantly. Is there a way for it to be dismissed with animation? // If I swap bottom sheet for
AlertDialog
then everything works as expected, so I don't think my usage of the API is not unreasonable
🧵 2
s

Stylianos Gakis

03/24/2024, 12:50 AM
Please keep long posts especially with big code snippets inside a thread. If you remove your composable from composition completely then it will simply stop existing. If you want to give it the opportunity to animate out you need to instead of removing it from composition to just tell it that it should stop showing, while still keeping it around. You may also afterwards remove it from composition too, when the animation out has finished.
u

ursus

03/24/2024, 2:13 AM
well, AlertDialog does animate out even if controlled by a plain
if
anyways, having a plain
if
statement control it is from the official sample docs here https://developer.android.com/jetpack/compose/components/bottom-sheets any idea as how to do what you're suggesting?
s

Stylianos Gakis

03/24/2024, 12:34 PM
Dialog is using a different mechanism to show on the screen. And I'd still suggest you edit your post to put the big code snippet inside the thread.
u

ursus

03/24/2024, 1:40 PM
Copy code
@Composable
fun Screen() {
    val showBottomSheet by viewModel.showBottomSheet.collectAsState()
    if (showBottomSheet) {
        val sheetState = rememberModalBottomSheetState()
        ModalBottomSheet(
            onDismissRequest = {
                viewModel.dismissBottomSheet()
            },
            sheetState = sheetState,
        ) {
            ...
        }
}

class MyViewModel {
    val _showBottomSheet = MutableStateFlow<Boolean>(false)
    val showBottomSheet: Flow<Boolean> get() = _showBottomSheet

    init {
       scope.launch {
           delay(5_000)
           _showBottomSheet.value = true <--------------------
           delay(5_000)
           _showBottomSheet.value = false <--------------------
       }
    }

    fun dismissBottomSheet() {
        _showBottomSheet.value = false
    }
}
here you go
so, how would I map the boolean onto
sheetState.hide/show
?
derivedStateOf somehow?
s

Stylianos Gakis

03/24/2024, 6:15 PM
derivedStateOf is definitely not what you want here. You can do a launchedEffect keyed on
showBottomSheet
and depending on what it is show or hide accordingly. Then make sure to keep the state object and then composable call itself outside of an if statement so that it's always present in composition. That can be a decent way to start with this, and you can adjust accordingly if you get it to work.
u

ursus

03/24/2024, 8:06 PM
doesnt work if I remove the if, then the modal is implicitly shown
Copy code
Column {
    var showModal by remember { mutableStateOf(false) }
    PrimaryPositiveButton(
        modifier = Modifier.fillMaxWidth(),
        text = "Open"
    ) {
        Log.d("Default", "Show")
        showModal = true
    }

    val sheetState = rememberModalBottomSheetState()
    LaunchedEffect(key1 = showModal) {
        Log.d("Default", "x=$showModal")
        if (showModal) {
            sheetState.show()
        } else {
            sheetState.hide()
        }
    }

    ModalBottomSheet(
        sheetState = sheetState,
        onDismissRequest = { showModal = false }
    ) {
        Text("Hello")
        PrimaryPositiveButton(
            modifier = Modifier.fillMaxWidth(),
            text = "Click"
        ) {
            Log.d("Default", "Close")
            showModal = false
        }
    }
}
and when I hit the button in the sheet, I see "Close" printed, but no recomposition is happening