Skip to content

Instantly share code, notes, and snippets.

@sdetilly
Created October 3, 2025 18:42
Show Gist options
  • Select an option

  • Save sdetilly/ed08589d52985fbc25a7d0a6e55c6f10 to your computer and use it in GitHub Desktop.

Select an option

Save sdetilly/ed08589d52985fbc25a7d0a6e55c6f10 to your computer and use it in GitHub Desktop.
How to use Animated visibility with null values
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.expandIn
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkOut
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
var user: User? by remember { mutableStateOf(null) }
Button(
onClick = {
user = if (user == null) User("John Doe", "[email protected]") else null
}
) {
Text("Toggle User Panel")
}
Spacer(Modifier.height(16.dp))
UserPanelDemo(user)
}
}
}
}
@Composable
fun UserPanelDemo(selectedUser: User?) {
AnimatedVisibilityWithNullable(nullableValue = selectedUser) { user ->
Card(
modifier = Modifier.fillMaxWidth()
.padding(horizontal = 24.dp),
) {
Column(Modifier.padding(16.dp)) {
Text("Name: ${user.name}")
Text("Email: ${user.email}")
}
}
}
}
@Composable
fun <T : Any> AnimatedVisibilityWithNullable(
modifier: Modifier = Modifier,
nullableValue: T?,
enter: EnterTransition = fadeIn() + expandIn(),
exit: ExitTransition = shrinkOut() + fadeOut(),
label: String = "AnimatedVisibilityNullable",
content: @Composable AnimatedVisibilityScope.(T) -> Unit,
) {
val visible = nullableValue != null
val remembered = remember(key1 = Unit) { mutableStateOf(nullableValue) }
if (
(remembered.value == null && nullableValue != null) ||
(remembered.value != null && nullableValue != null && remembered.value != nullableValue)
) {
remembered.value = nullableValue
}
val stateCleanNeeded = remembered.value != null && nullableValue == null
AnimatedVisibility(
visible = visible,
modifier = modifier,
enter = enter,
exit = exit,
label = label,
) {
remembered.value?.let {
content(it)
}
DisposableEffect(key1 = stateCleanNeeded) {
onDispose {
if (stateCleanNeeded) {
remembered.value = null
}
}
}
}
}
data class User(val name: String, val email: String)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment