animationSpec: FiniteAnimationSpec<Float> = tween(), contentKey: (targetState: T) -> Any? = { it }, content: @Composable (targetState: T) -> Unit ) { val currentlyVisible = remember { mutableStateListOf<T>().apply { add(currentState) } } val contentMap = remember { mutableMapOf<T, @Composable () -> Unit>() } if (currentState == targetState) { // If not animating, just display the current state if (currentlyVisible.size != 1 || currentlyVisible[0] != targetState) { // Remove all the intermediate items from the list once the animation is finished. currentlyVisible.removeAll { it != targetState } contentMap.clear() } }
= tween(), contentKey: (targetState: T) -> Any? = { it }, content: @Composable (targetState: T) -> Unit ) { val currentlyVisible = remember { mutableStateListOf<T>().apply { add(currentState) } } val contentMap = remember { mutableMapOf<T, @Composable () -> Unit>() } if (currentState == targetState) { // If not animating, just display the current state if (currentlyVisible.size != 1 || currentlyVisible[0] != targetState) { // Remove all the intermediate items from the list once the animation is finished. currentlyVisible.removeAll { it != targetState } contentMap.clear() } } Compose 1.6
val contentMap = remember { mutableMapOf<T, @Composable () -> Unit>() } if (currentState == targetState) { // If not animating, just display the current state if (currentlyVisible.size != 1 || currentlyVisible[0] != targetState) { // Remove all the intermediate items from the list once the animation is finished. currentlyVisible.removeAll { it != targetState } contentMap.clear() } } if (!contentMap.contains(targetState)) { // Replace target with the same key if any val replacementId = currentlyVisible.indexOfFirst { contentKey(it) == contentKey(targetState) } if (replacementId == -1) { currentlyVisible.add(targetState) } else { currentlyVisible[replacementId] = targetState } contentMap.clear() Compose 1.6
-> ContentTransform = { ... }, contentAlignment: Alignment = Alignment.TopStart, contentKey: (targetState: S) -> Any? = { it }, content: @Composable AnimatedContentScope.(targetState: S) -> Unit ) { val layoutDirection = LocalLayoutDirection.current val rootScope = remember(this) { AnimatedContentTransitionScopeImpl(this, contentAlignment, layoutDirection) } // TODO: remove screen as soon as they are animated out val currentlyVisible = remember(this) { mutableStateListOf(currentState) } val contentMap = remember(this) { mutableMapOf<S, @Composable() () -> Unit>() } // This is needed for tooling because it could change currentState directly, // as opposed to changing target only. When that happens we need to clear all the // visible content and only display the content for the new current state and target state. if (!currentlyVisible.contains(currentState)) { Compose 1.6
@Composable AnimatedContentScope.(targetState: S) -> Unit ) { val layoutDirection = LocalLayoutDirection.current val rootScope = remember(this) { AnimatedContentTransitionScopeImpl(this, contentAlignment, layoutDirection) } // TODO: remove screen as soon as they are animated out val currentlyVisible = remember(this) { mutableStateListOf(currentState) } val contentMap = remember(this) { mutableMapOf<S, @Composable() () -> Unit>() } // This is needed for tooling because it could change currentState directly, // as opposed to changing target only. When that happens we need to clear all the // visible content and only display the content for the new current state and target state. if (!currentlyVisible.contains(currentState)) { currentlyVisible.clear() currentlyVisible.add(currentState) } if (currentState == targetState) { if (currentlyVisible.size != 1 || currentlyVisible[0] != currentState) { currentlyVisible.clear() currentlyVisible.add(currentState) } if (contentMap.size != 1 || contentMap.containsKey(currentState)) { Compose 1.6
target with the same key if any val id = currentlyVisible.indexOfFirst { contentKey(it) == contentKey(targetState) } if (id == -1) { currentlyVisible.add(targetState) } else { currentlyVisible[id] = targetState } } if (!contentMap.containsKey(targetState) || !contentMap.containsKey(currentState)) { contentMap.clear() currentlyVisible.fastForEach { stateForContent -> contentMap[stateForContent] = { val specOnEnter = remember { transitionSpec(rootScope) } // NOTE: enter and exit for this AnimatedVisibility will be using different spec, // naturally. val exit = remember(segment.targetState == stateForContent) { if (segment.targetState == stateForContent) { ExitTransition.None } else { rootScope.transitionSpec().initialContentExit } Compose 1.6
target with the same key if any val id = currentlyVisible.indexOfFirst { contentKey(it) == contentKey(targetState) } if (id == -1) { currentlyVisible.add(targetState) } else { currentlyVisible[id] = targetState } } if (!contentMap.containsKey(targetState) || !contentMap.containsKey(currentState)) { contentMap.clear() currentlyVisible.fastForEach { stateForContent -> contentMap[stateForContent] = { val specOnEnter = remember { transitionSpec(rootScope) } // NOTE: enter and exit for this AnimatedVisibility will be using different spec, // naturally. val exit = remember(segment.targetState == stateForContent) { if (segment.targetState == stateForContent) { ExitTransition.None } else { rootScope.transitionSpec().initialContentExit } Compose 1.6
target with the same key if any val id = currentlyVisible.indexOfFirst { contentKey(it) == contentKey(targetState) } if (id == -1) { currentlyVisible.add(targetState) } else { currentlyVisible[id] = targetState } } if (!contentMap.containsKey(targetState) || !contentMap.containsKey(currentState)) { contentMap.clear() currentlyVisible.fastForEach { stateForContent -> contentMap[stateForContent] = { val specOnEnter = remember { transitionSpec(rootScope) } // NOTE: enter and exit for this AnimatedVisibility will be using different spec, // naturally. val exit = remember(segment.targetState == stateForContent) { if (segment.targetState == stateForContent) { ExitTransition.None } else { rootScope.transitionSpec().initialContentExit } Compose 1.6
{ AnimatedContentTransitionScopeImpl.ChildData(stateForContent == targetState) } // TODO: Will need a custom impl of this to: 1) get the signal for when // the animation is finished, 2) get the target size properly AnimatedEnterExitImpl( this, { it == stateForContent }, enter = specOnEnter.targetContentEnter, exit = exit, modifier = Modifier .layout { measurable, constraints -> val placeable = measurable.measure(constraints) layout(placeable.width, placeable.height) { placeable.place(0, 0, zIndex = specOnEnter.targetContentZIndex) } } .then(childData.apply { isTarget = stateForContent == targetState }), shouldDisposeBlock = { currentState, targetState -> Compose 1.6
-> Unit ) { SharedTransitionScope { sharedTransitionModifier -> // Put shared transition modifier *after* user provided modifier to support user provided // modifiers to influence the overlay's size, position, clipping, etc. Box(modifier.then(sharedTransitionModifier)) { content() } } } Compose 1.7
() -> Unit) { // Safely update the current `onBack` lambda when a new one is provided val currentOnBack by rememberUpdatedState(onBack) // Remember in Composition a back callback that calls the `onBack` lambda val backCallback = remember { object : OnBackPressedCallback(enabled) { override fun handleOnBackPressed() { currentOnBack() } } } // On every successful composition, update the callback with the `enabled` value SideEffect { backCallback.isEnabled = enabled } val backDispatcher = checkNotNull(LocalOnBackPressedDispatcherOwner.current) { "No OnBackPressedDispatcherOwner was provided via LocalOnBackPressedDispatcherOwner” }.onBackPressedDispatcher
() -> Unit) { // Safely update the current `onBack` lambda when a new one is provided val currentOnBack by rememberUpdatedState(onBack) // Remember in Composition a back callback that calls the `onBack` lambda val backCallback = remember { object : OnBackPressedCallback(enabled) { // Android only override fun handleOnBackPressed() { currentOnBack() } } } // On every successful composition, update the callback with the `enabled` value SideEffect { backCallback.isEnabled = enabled } val backDispatcher = checkNotNull(LocalOnBackPressedDispatcherOwner.current) { "No OnBackPressedDispatcherOwner was provided via LocalOnBackPressedDispatcherOwner” }.onBackPressedDispatcher
ContainerTransform AnimatedContent Shared Elements Predictive Back Pre-release NavHost Enter/Exit Transition List-Detail NavHost Enter/Exit Transition List Sort/Type Enter/Exit Transition AnimatedContent Tab-Contents Crossfade Fade Through or