Reputation: 60081
In https://developer.android.com/jetpack/compose/animation, it is mentioned that
We sometimes want to have an initial state different from the first target state. We can use updateTransition
with MutableTransitionState
to achieve this. For example, it allows us to start animation as soon as the code enters composition.
// Start in collapsed state and immediately animate to expanded
var currentState = remember { MutableTransitionState(BoxState.Collapsed) }
currentState.targetState = BoxState.Expanded
val transition = updateTransition(currentState)
// ...
(For full code, refer to full code example 1. below)
However, the above code is only working for initial animation but not subsequent (i.e. change from Expanded
to Collapsed
), as whenever the currentState.targetState
changes, the composable function will run and retriggered currentState.targetState = BoxState.Expanded
.
To fix the problem, I'll have to
var currentState by remember { mutableStateOf(BoxState.Collapsed) }
val transition = updateTransition(currentState, label = "")
LaunchedEffect(Unit) {
currentState.targetState = BoxState.Expanded
}
(For full code, refer to full code example 2. below)
This will make the animation start automatically from Collapsed
to Expanded
. Also subsequently upon change from Expanded
to Collapse
(e.g. through a button click), it will still work, as the LaunchedEffect
is no longer triggered.
With the LaunchedEffect
, we can also do it with normal mutableStateOf
, i.e. without MutableTransitionState
, and still, behave well.
var currentState by remember { mutableStateOf(BoxState.Collapsed) }
val transition = updateTransition(currentState, label = "")
LaunchedEffect(Unit) {
currentState = BoxState.Expanded
}
(For full code, refer to full code example 3. below)
So I cannot see any extra benefit using MutableTransitionState
compare to the normal mutableStateOf
. Anything I miss?
The code that doesn't works with just MutableTransitionState
(i.e. no effect on button click, only having initial animation)
@Composable
fun Greeting() {
val currentState = remember { MutableTransitionState(BoxState.Collapsed) }
currentState.targetState = BoxState.Expanded
val transition = updateTransition(currentState, label = "")
val rect by transition.animateRect(label = "") { state ->
when (state) {
BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f)
BoxState.Expanded -> Rect(100f, 100f, 300f, 300f)
}
}
Column {
Canvas(
modifier = Modifier.fillMaxWidth().height(500.dp)
.border(BorderStroke(1.dp, Color.Green))
) {
drawPath(Path().apply { addRect(rect) }, Color.Red)
}
Button(onClick = {
currentState.targetState =
if (currentState.targetState == BoxState.Expanded) BoxState.Collapsed
else BoxState.Expanded
}) {
Text("Click Me")
}
}
}
The code that works with just MutableTransitionState
(i.e. have effect on button click, and having initial animation)
@Composable
fun Greeting() {
val currentState = remember { MutableTransitionState(BoxState.Collapsed) }
val transition = updateTransition(currentState, label = "")
val rect by transition.animateRect(label = "") { state ->
when (state) {
BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f)
BoxState.Expanded -> Rect(100f, 100f, 300f, 300f)
}
}
Column {
Canvas(
modifier = Modifier.fillMaxWidth().height(500.dp)
.border(BorderStroke(1.dp, Color.Green))
) {
drawPath(Path().apply { addRect(rect) }, Color.Red)
}
Button(onClick = {
currentState.targetState =
if (currentState.targetState == BoxState.Expanded) BoxState.Collapsed
else BoxState.Expanded
}) {
Text("Click Me")
}
}
LaunchedEffect(Unit) {
currentState.targetState = BoxState.Expanded
}
}
The code that works with just mutableStateOf
(i.e. have effect on button click, and having initial animation)
@Composable
fun Greeting() {
var currentState by remember { mutableStateOf(BoxState.Collapsed) }
val transition = updateTransition(currentState, label = "")
val rect by transition.animateRect(label = "") { state ->
when (state) {
BoxState.Collapsed -> Rect(0f, 0f, 100f, 100f)
BoxState.Expanded -> Rect(100f, 100f, 300f, 300f)
}
}
Column {
Canvas(
modifier = Modifier.fillMaxWidth().height(500.dp)
.border(BorderStroke(1.dp, Color.Green))
) {
drawPath(Path().apply { addRect(rect) }, Color.Red)
}
Button(onClick = {
currentState =
if (currentState == BoxState.Expanded) BoxState.Collapsed
else BoxState.Expanded
}) {
Text("Click Me")
}
}
LaunchedEffect(Unit) {
currentState = BoxState.Expanded
}
}
Upvotes: 2
Views: 1508
Reputation: 987
MutableTransitionState
allows you to specify a different targetState than the initial state. Note it is not a MutableState
.
If you do something like this:
val currentState = remember {
MutableTransitionState(BoxState.Collapsed).apply {targetState = BoxState.Expanded}
}
You can expect the Transition
to start an animation going from BoxState.Collapsed
to BoxState.Expanded
as soon as the updateTransition
is composed for the first time.
MutableTransitionState
is designed to 1) trigger animations when a composable gets added to the tree (i.e. enter animations), and 2) allow observation of currentState vs targetState through the MutableTransitionState
object like this: https://android.googlesource.com/platform/frameworks/support/+/e6095adbb8ffba6aede464fd06ef7302eac61860/compose/animation/animation/integration-tests/animation-demos/src/main/java/androidx/compose/animation/demos/AnimatedVisiblilityLazyColumnDemo.kt#127
Upvotes: 3