how to display a detail in a list-detail scheme wi...
# compose
p
how to display a detail in a list-detail scheme with an animation? I tryed this, but is not working, the PlaceDetail composable is appearing instantly:
Copy code
if (uiState.selectedPlace != null) {
            AnimatedVisibility(true) {
                PlaceDetail(
                    place = uiState.selectedPlace,
                    modifier = modifier
                )
            }
        } else {
            ListScreen(
                title = stringResource(id = uiState.selectedCategory?.nameResource ?: R.string.empty_string),
                listItems = uiState.placesList,
                selectedItem = uiState.selectedPlace,
                onItemClicked = {
                    viewModel.updateCurrentPlace(it as Place)
                },
                modifier = modifier
            )
        }
s
For an AnimatedVisibility to actually be able to animate both in and out, it needs to be in composition in both cases. Now when
uiState.selectedPlace
is null, AnimatedVisibility just leaves composition completely. If you tried something like
Copy code
Row {
    ListScreen(
        title = stringResource(id = uiState.selectedCategory?.nameResource ?: R.string.empty_string),
        listItems = uiState.placesList,
        selectedItem = uiState.selectedPlace,
        onItemClicked = {
            viewModel.updateCurrentPlace(it as Place)
        },
        modifier = modifier
    )
    AnimatedVisibility(uiState.selectedPlace != null) {
        PlaceDetail(
            place = uiState.selectedPlace,
            modifier = modifier
        )
    }
}
You’re probably closer to what you want to do here. Can read more here
p
does exist a way to animate just all the content of the composable without depending on a state? I mean, simply animate a fade in for all the content, always that the composable is being displayed
I tryed your sample but doesn't works, also, take in mind that if the place is not selected, the list must appear with an animation, and if the place is selected, the list must dissapear and the detail must appear with an animation
well finally I did it understanding your proposal but changing some code, I must add an animatedvisibility for the if and the inverse for the else:
Copy code
AnimatedVisibility(
            visible = uiState.selectedPlace != null,
            enter = fadeIn(),
            exit = fadeOut()
        ) {
            PlaceDetail(
                place = uiState.selectedPlace,
                onBackPressed = { viewModel.updateCurrentPlace(null) },
                modifier = modifier
            )
        }

        AnimatedVisibility(
            visible = uiState.selectedPlace == null,
            enter = fadeIn(),
            exit = fadeOut()
        ) {
            ListScreen(
                title = stringResource(
                    id = uiState.selectedCategory?.nameResource ?: R.string.empty_string
                ),
                listItems = uiState.placesList,
                selectedItem = uiState.selectedPlace,
                onItemClicked = {
                    viewModel.updateCurrentPlace(it as Place)
                },
                modifier = modifier
            )
        }
feels strange to be forzed to do this way
whould be much more simple if compose gives the possibility to simply animate all the content of a composable without depending on a state variable, just displaying it with the animation always, everything on it
s
AnimatedContent does that, animate everything you put inside there if you want that
In any case, you need some state to know what to show, otherwise how do you even go from state A to state B?
p
mmmm... I think there is a way, simply animating the call to the composable, instead of animating the content of the composable
maybe
s
Also if those screens that you got are navigation destinations in androidx.navigation, you can use their APIs to provide animation of how things should appear/disappear. But if it’s not the case ignore my case
p
bytheway, the fadein animation is not equal to the default animation of navigating to a new composable screen
in this case, navigation is not being used for display the detail
but navigation is being used to arrive to the list-detail
and my intention was to put exactly the same animantion for displaying the detail
but is not the same:
Copy code
AnimatedVisibility(
    visible = uiState.selectedPlace != null,
    enter = fadeIn(),
    exit = fadeOut()
) {
it is more faster, and I can't find in google how to reproduce the exact animation of navigation
p
wow! thank you! great
there is a difference bytheway, when pressing back, instead of going from fadeout on the detail to fadein on the list, the detail goes to white instantly, and then the list does a fadein
it's strange
the code seems to be OK
Copy code
AnimatedVisibility(
            visible = uiState.selectedPlace != null,
            enter = fadeIn(animationSpec = tween(700)),
            exit = fadeOut(animationSpec = tween(700))
        ) {
            PlaceDetail(
                place = uiState.selectedPlace,
                onBackPressed = { viewModel.updateCurrentPlace(null) },
                modifier = modifier
            )
        }

        AnimatedVisibility(
            visible = uiState.selectedPlace == null,
            enter = fadeIn(animationSpec = tween(700)),
            exit = fadeOut(animationSpec = tween(700))
        ) {
            ListScreen(
                title = stringResource(
                    id = uiState.selectedCategory?.nameResource ?: R.string.empty_string
                ),
                listItems = uiState.placesList,
                selectedItem = uiState.selectedPlace,
                onItemClicked = {
                    viewModel.updateCurrentPlace(it as Place)
                },
                modifier = modifier
            )
        }
it is like if it's ignoring the exit animation
s
What do you mean by “press back” in this context? Are you intercepting the back gesture with a
BackHandler
call?
p
yes, the back it's setting the selectedPlace to null, to go back from detail to the list
Copy code
onBackPressed = { viewModel.updateCurrentPlace(null) },
s
Alright. Then it’s probably the fact that you are using
uiState.selectedPlace
inside the ListScreen for example. but as you are animating out, it is actually null. So maybe the way you got the composable to render in that scenario you just show nothing. It’s a guess, but it’d be funny if that’s it since it’s literally the thread above this one https://kotlinlang.slack.com/archives/CJLTWPH7S/p1710944106432999
p
well I certainly don't know how to adapt my code to use AnimatedContent, can't make it work with this
Copy code
AnimatedContent(targetState = uiState.selectedPlace != null) { targetCount ->
            PlaceDetail(
                place = uiState.selectedPlace,
                onBackPressed = { viewModel.updateCurrentPlace(null) },
                modifier = modifier
            )
        }
also tried with that proposed by Mofe Ejegi and didn't work too
s
Copy code
AnimatedContent(targetState = uiState.selectedPlace) { selectedPlace ->
  if (selectedPlace != null) {
    PlaceDetail(
      place = uiState.selectedPlace,
      onBackPressed = { viewModel.updateCurrentPlace(null) },
      modifier = modifier
    )
  } else {
    ListScreen(
      title = stringResource(
        id = uiState.selectedCategory?.nameResource ?: R.string.empty_string
      ),
      listItems = uiState.placesList,
      selectedItem = uiState.selectedPlace,
      onItemClicked = {
        viewModel.updateCurrentPlace(it as Place)
      },
      modifier = modifier
    )
  }
}
Try something more like this And add the right animation specs etc
p
umm, the problem persists
s
Copy code
AnimatedContent(targetState = uiState) { uiState ->
  if (uiState.selectedPlace != null) {
    PlaceDetail(
      place = uiState.selectedPlace,
      onBackPressed = { viewModel.updateCurrentPlace(null) },
      modifier = modifier
    )
  } else {
    ListScreen(
      title = stringResource(
        id = uiState.selectedCategory?.nameResource ?: R.string.empty_string
      ),
      listItems = uiState.placesList,
      selectedItem = uiState.selectedPlace,
      onItemClicked = {
        viewModel.updateCurrentPlace(it as Place)
      },
      modifier = modifier
    )
  }
}
Actually that’s what you want here. I was wrongly using the uiState which came from outside the AnimatedContent, but you actually need to use the one provided by the lambda to get the right behavior
1
p
perfect! thank you
s
This is what I meant here https://kotlinlang.slack.com/archives/CJLTWPH7S/p1710975332221569?thread_ts=1710973492.568769&cid=CJLTWPH7S btw. And what the thread right above this one discusses exactly
👍 1