In this post will be showing you how I created a simple transition animation in Android Compose.
In my example, I wanted to create a transition animation in Android for a TextField composable. The element needed to be contained in a Row with a button.
Android Compose Transition Animations
Process
- When the button is clicked, the Row will come into the middle of the screen.
- The Row will change it's child TextField visibility by expanding the Row composable.
- Vice versa when button is clicked.
Code
MainActivity
This is the MainActivity that points to the custom UI element of SlideInOverlay. The SlideInOverlay has a callback that captures the text written in TextField back to the parent element to display in the parent elements Text element on screen. Simple. The callback could be used to send elsewhere depending on your situation.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SlidingInMessageTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
var message by remember { mutableStateOf("") }
Box(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
) {
Text(modifier = Modifier.align(Alignment.Center), text = message)
SlideInOverlay(
callback = { msg ->
message = msg
}
)
}
}
}
}
}
}SlideInOverlay
The SlideInOverlay function contains a root element of box that has the Row element inside. It is positioned to bottom right corner using Offsets. When the IconButton is clicked, then the transition animation executed based on the state provided.
@Composable
fun SlideInOverlay(callback: (String) -> Unit) {
val density = LocalDensity.current
var size by remember { mutableStateOf(Size.Zero) }
Box(
modifier = Modifier
.fillMaxSize()
.onGloballyPositioned {
size = Size(
with(density) { it.size.width.toDp() }.value,
with(density) { it.size.height.toDp() }.value
)
}
) {
var slideInState by remember { mutableStateOf(false) }
val transition = updateTransition(slideInState)
val slidingIn by transition.animateOffset(transitionSpec = { tween(750) }) {
if (!it) Offset(size.width - 48f, 0f) else Offset(0f, -size.height / 3)
}
var message by remember { mutableStateOf("") }
if(!slideInState){
message = ""
}
Row(
modifier = Modifier
.fillMaxWidth()
.offset(x = slidingIn.x.dp, y = slidingIn.y.dp)
.align(Alignment.BottomEnd)
) {
IconButton(
modifier = Modifier
.size(48.dp)
.clip(shape = CircleShape),
onClick = {
slideInState = !slideInState
}) {
Icon(imageVector = Icons.Sharp.Email, contentDescription = null)
}
transition.AnimatedVisibility(
visible = { slideInState -> slideInState },
enter = expandHorizontally(tween(1000)),
exit = shrinkHorizontally(tween(500))
) {
TextField(
modifier = Modifier.width(IntrinsicSize.Max).focusable(slideInState),
value = message,
onValueChange = {
message = it
callback(message)
})
}
IconButton(
modifier = Modifier
.size(48.dp)
.clip(shape = CircleShape),
onClick = {
}) {
Icon(imageVector = Icons.AutoMirrored.Sharp.Send, contentDescription = null)
}
}
}
}Feel free to give it a try and update depending on your use case.

