brandonmcansh
07/01/2021, 1:16 PMbrandonmcansh
07/01/2021, 1:19 PM@Composable
private fun ButtonRow(
constraints: BoxWithConstraintsScope,
downloadState: DownloadState,
actioner: (AppDetailsAction) -> Unit
) {
Row(modifier = Modifier.fillMaxWidth()) {
val transitionData =
updateTransitionData(constraints = constraints, downloadState = downloadState)
when (downloadState) {
Queued -> {
CancelButton(
modifier = Modifier.height(transitionData.leftButtonHeight)
.width(transitionData.leftButtonWidth),
actioner = actioner
)
Spacer(modifier = Modifier.padding(transitionData.spacerWidth))
QueuedButton(modifier = Modifier.width(transitionData.rightButtonWidth))
}
NotInstalled -> {
CancelButton(
modifier = Modifier.height(transitionData.leftButtonHeight)
.width(transitionData.leftButtonWidth),
enabled = false,
actioner = actioner
)
Spacer(modifier = Modifier.padding(transitionData.spacerWidth))
InstallButton(modifier = Modifier.width(transitionData.rightButtonWidth), actioner = actioner)
}
}
}
}
brandonmcansh
07/01/2021, 1:20 PMI'm specifying the transition as:
private class TransitionData(
leftButtonHeight: State<Dp>,
leftButtonWidth: State<Dp>,
rightButtonWidth: State<Dp>,
spacerWidth: State<Dp>,
) {
val leftButtonHeight by leftButtonHeight
val leftButtonWidth by leftButtonWidth
val rightButtonWidth by rightButtonWidth
val spacerWidth by spacerWidth
}
@Composable
private fun updateTransitionData(
constraints: BoxWithConstraintsScope,
downloadState: DownloadState
): TransitionData {
val spacerSize: Dp = 16.dp
val buttonWidth = (constraints.maxWidth / 2) - spacerSize
val transition = updateTransition(targetState = downloadState, label = "transition")
val leftButtonHeight = transition.animateDp(
transitionSpec = { tween(1000) },
label = "leftHeight"
) { state ->
when (state) {
NotInstalled -> 0.dp
Queued -> ButtonDefaults.MinHeight
}
}
val leftButtonWidth = transition.animateDp(
transitionSpec = { tween(1000) },
label = "leftWidth"
) { state ->
when (state) {
NotInstalled -> 0.dp
Queued -> buttonWidth
}
}
val rightButtonWidth = transition.animateDp(
transitionSpec = { tween(1000) },
label = "rightWidth"
) { state ->
when (state) {
NotInstalled -> constraints.maxWidth
Queued -> buttonWidth
}
}
val spacerWidth = transition.animateDp(
transitionSpec = { tween(1000) },
label = "spacer") { state ->
when (state) {
NotInstalled -> 0.dp
Queued -> spacerSize
}
}
return remember(transition) { TransitionData(leftButtonHeight, leftButtonWidth, rightButtonWidth, spacerWidth) }
}
brandonmcansh
07/01/2021, 1:22 PM@Composable
private fun CancelButton(
modifier: Modifier = Modifier,
enabled: Boolean = true,
actioner: (AppDetailsAction) -> Unit
) {
ThemedButton(
modifier = modifier,
enabled = enabled,
outlined = true,
onClick = { actioner(AppDetailsAction.CancelDownload) }
) {
Text(text = stringResource(id = android.R.string.cancel))
}
}
@Composable
private fun InstallButton(
modifier: Modifier = Modifier,
actioner: (AppDetailsAction) -> Unit
) {
ThemedButton(
modifier = modifier,
onClick = { actioner(AppDetailsAction.Install) }
) {
Text(text = stringResource(id = R.string.install))
}
}
@Composable
private fun QueuedButton(
modifier: Modifier = Modifier,
) {
ThemedButton(
modifier = modifier,
enabled = false,
onClick = { }
) {
Text(text = stringResource(id = R.string.queued))
}
}
Halil Ozercan
07/01/2021, 4:48 PMupdateTransitionData
. It is being remembered as long as transition object is the same. I'm not sure if updateTransition
had an internal remember or you are supposed to remember it but somethings might be going wrong there. Just a suspicion.brandonmcansh
07/01/2021, 5:05 PMHalil Ozercan
07/01/2021, 5:10 PMDoris Liu
07/01/2021, 6:26 PMDownloadState
? If it gets recreated each frame, it'll be treated as an interruption to the previous animation, and transition will use spring to handle that interruption.brandonmcansh
07/01/2021, 6:56 PM2021-07-01 09:47:46.205 27469-27469 D/buttons: downloadState=com.gabb.pkgmgmt.data.NotInstalled@fa4991c
2021-07-01 09:47:46.205 27469-27469 D/buttons: left width=0.0.dp
2021-07-01 09:47:50.230 27469-27469 D/buttons: downloadState=com.gabb.pkgmgmt.data.Queued@869d191
2021-07-01 09:47:50.232 27469-27469 D/buttons: left width=157.71428.dp
2021-07-01 09:47:52.312 27469-27469 D/buttons: downloadState=com.gabb.pkgmgmt.data.NotInstalled@fa4991c
2021-07-01 09:47:52.313 27469-27469 D/buttons: left width=0.0.dp
Doris Liu
07/01/2021, 7:01 PMupdateTransition
?brandonmcansh
07/01/2021, 7:01 PMbrandonmcansh
07/01/2021, 7:01 PMbrandonmcansh
07/01/2021, 7:01 PMDoris Liu
07/01/2021, 7:06 PMTransitionData
herebrandonmcansh
07/01/2021, 7:06 PMDoris Liu
07/01/2021, 7:09 PMupdateTransition
does internally remember the transition object. But if there's a key {...}
up the tree, it'll recreate the object when the key changes.Doris Liu
07/01/2021, 7:15 PMupdateTransition
has changed somewhere in the call stack, the updateTransition will be treated as a new Composable added to the tree. So if you have:
if (foo) { Bar(baz1) } else Bar(baz2)
where Bar directly or indirectly calls into updateTransition
, you'll get a brand new transition.brandonmcansh
07/01/2021, 7:16 PMbrandonmcansh
07/01/2021, 7:16 PMbrandonmcansh
07/01/2021, 7:20 PMbrandonmcansh
07/01/2021, 7:20 PMbrandonmcansh
07/01/2021, 7:20 PMbrandonmcansh
07/01/2021, 7:21 PMbrandonmcansh
07/01/2021, 7:21 PMDoris Liu
07/01/2021, 7:21 PMbrandonmcansh
07/01/2021, 7:33 PM