Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Layout Animations Show - KotlinConf2023

Layout Animations Show - KotlinConf2023

Nicole Terc

April 25, 2023
Tweet

More Decks by Nicole Terc

Other Decks in Technology

Transcript

  1. States var viewColor by remember { mutableStateOf(Color.Blue) } var viewScale

    by remember { mutableStateOf(0.5f) } Box( modifier = Modifier .scale(viewScale) .background(viewColor) )
  2. Composable APIs Composable functions that handle all the animation logic

    AnimationComposable() { <- Does all the work! AmazingUI( ... ) }
  3. Composable APIs Composable functions that handle all the animation logic

    AnimationComposable(targetState = state) { <- Does all the work! AmazingUI( ... ) }
  4. Animated Visibility - MORE! - FUN! @Composable fun AnimatedVisibility( visible:

    Boolean, modifier: Modifier = Modifier, enter: EnterTransition = fadeIn() + expandVertically(), exit: ExitTransition = fadeOut() + shrinkVertically(), label: String = "AnimatedVisibility", content: @Composable AnimatedVisibilityScope.() -> Unit ) { val transition = updateTransition(visible, label) AnimatedEnterExitImpl(transition, { it }, ... ) }
  5. Animated Visibility - MORE! @Composable fun AnimatedVisibility( visible: Boolean, modifier:

    Modifier = Modifier, enter: EnterTransition = fadeIn() + expandVertically(), exit: ExitTransition = fadeOut() + shrinkVertically(), label: String = "AnimatedVisibility", content: @Composable AnimatedVisibilityScope.() -> Unit ) { val transition = updateTransition(visible, label) AnimatedEnterExitImpl(transition, { it }, ... ) }
  6. Transitions Define how a state change is animated Contain: •

    The current state • The target state • The animation(s) to play
  7. Transitions - Enter & Exit var isVisible by remember {

    mutableStateOf(true) } AnimatedVisibility( visible = isVisible, ) { ... }
  8. Transitions - Enter & Exit var isVisible by remember {

    mutableStateOf(true) } AnimatedVisibility( visible = isVisible, enter = slideInHorizontally(), exit = slideOutHorizontally(), ) { ... }
  9. Transitions - Enter & Exit var isVisible by remember {

    mutableStateOf(true) } AnimatedVisibility( visible = isVisible, enter = scaleIn(), exit = scaleOut(), ) { ... }
  10. Transitions - Enter & Exit var isVisible by remember {

    mutableStateOf(true) } AnimatedVisibility( visible = isVisible, enter = scaleIn() + slideInHorizontally(), exit = scaleOut() + slideOutHorizontally(), ) { ... }
  11. Transitions - MORE! @Composable fun AnimatedVisibility( visible: Boolean, modifier: Modifier

    = Modifier, enter: EnterTransition = fadeIn() + expandVertically(), exit: ExitTransition = fadeOut() + shrinkVertically(), label: String = "AnimatedVisibility", content: @Composable AnimatedVisibilityScope.() -> Unit ) { val transition = updateTransition(visible, label) AnimatedEnterExitImpl(transition, { it }, ... ) }
  12. Transitions - MORE! @Composable fun AnimatedVisibility( visible: Boolean, modifier: Modifier

    = Modifier, enter: EnterTransition = fadeIn() + expandVertically(), exit: ExitTransition = fadeOut() + shrinkVertically(), label: String = "AnimatedVisibility", content: @Composable AnimatedVisibilityScope.() -> Unit ) { val transition = updateTransition(visible, label) AnimatedEnterExitImpl(transition, { it }, ... ) }
  13. Transitions - MORE! AnimatedVisibility(...) { val color by transition.animateColor() {

    tState -> if (tState == EnterExitState.Visible) { TileColor.Green } else { TileColor.Red } } }
  14. Transitions - MORE! AnimatedVisibility(...) { val color by transition.animateColor() {

    tState -> if (tState == EnterExitState.Visible) { TileColor.Green } else { TileColor.Red } } ColorScreen(color = color) { Text(text = "I'm visible! :D") } }
  15. Transitions - MORE! AnimatedVisibility(...) { val color by transition.animateColor() {

    ... } val progress by transition.animateFloat(){ tState -> if (tState == EnterExitState.Visible) 1f else 0f } ColorScreen(color = color) { Text(text = if (progress == 1f) "I'm visible! :D" else "D:" ) } }
  16. Transitions - MORE! AnimatedVisibility(...) { val color by transition.animateColor() {

    ... } val progress by transition.animateFloat(){...} val roundCorners by transition.animateDp { state -> if (state == EnterExitState.Visible) 0.dp else 500.dp } ColorScreen(Modifier.clip(RoundedCornerShape(...))) { Text(text = if (progress == 1f) "I'm visible! :D" else "D:" ) } }
  17. Crossfade @Composable fun <T> Crossfade( targetState: T, modifier: Modifier =

    Modifier, animationSpec: FiniteAnimationSpec<Float> = tween(), content: @Composable (T) -> Unit ) { val transition = updateTransition(targetState) transition.Crossfade(modifier, animationSpec, content = content) }
  18. Crossfade @Composable fun <T> Crossfade( targetState: T, modifier: Modifier =

    Modifier, animationSpec: FiniteAnimationSpec<Float> = tween(), content: @Composable (T) -> Unit ) { val transition = updateTransition(targetState) transition.Crossfade(modifier, animationSpec, content = content) }
  19. Crossfade @Composable fun <T> Crossfade( targetState: T, modifier: Modifier =

    Modifier, animationSpec: FiniteAnimationSpec<Float> = tween(), content: @Composable (T) -> Unit ) { val transition = updateTransition(targetState) transition.Crossfade(modifier, animationSpec, content = content) }
  20. Crossfade enum class Screen { ScreenOne, ScreenTwo, } var screen

    by remember { mutableStateOf(ScreenOne) }
  21. Crossfade var screen by remember { mutableStateOf(ScreenOne) } Crossfade( targetState

    = screen, ) { state -> when (state) { ScreenOne -> ScreenOne() ScreenTwo -> ScreenTwo() } }
  22. Crossfade var screen by remember { mutableStateOf(ScreenOne) } Crossfade( targetState

    = screen, ) { state -> when (state) { ScreenOne -> ScreenOne() ScreenTwo -> ScreenTwo() } }
  23. Crossfade @Composable fun <T> Crossfade( targetState: T, modifier: Modifier =

    Modifier, animationSpec: FiniteAnimationSpec<Float> = tween(), content: @Composable (T) -> Unit ) { val transition = updateTransition(targetState) transition.Crossfade(modifier, animationSpec, content = content) }
  24. Crossfade @Composable fun <T> Crossfade( targetState: T, modifier: Modifier =

    Modifier, animationSpec: FiniteAnimationSpec<Float> = tween(), content: @Composable (T) -> Unit ) { val transition = updateTransition(targetState) transition.Crossfade(modifier, animationSpec, content = content) }
  25. Crossfade @Composable fun <T> Crossfade( targetState: T, modifier: Modifier =

    Modifier, animationSpec: FiniteAnimationSpec<Float> = tween(), content: @Composable (T) -> Unit ) { val transition = updateTransition(targetState) transition.Crossfade(modifier, animationSpec, content = content) }
  26. AnimationSpec Defines how an animation runs. 1. Transitions ◦ Define

    the general path to follow (target state) ▪ Ej: slide to top left
  27. AnimationSpec Defines how an animation runs. 1. Transitions ◦ Define

    the general path to follow (target state) ▪ Ej: slide to top left 2. AnimationSpec ◦ Define how to follow that path
  28. AnimationSpec Defines how an animation runs. 1. Transitions ◦ Define

    the general path to follow (target state) ▪ Ej: slide to top left 2. AnimationSpec ◦ Define how to follow that path ▪ Ej: Go straight to the point
  29. AnimationSpec Defines how an animation runs. 1. Transitions ◦ Define

    the general path to follow (target state) ▪ Ej: slide to top left 2. AnimationSpec ◦ Define how to follow that path ▪ Ej: Go straight to the point ▪ Ej: First go super fast and then super slow
  30. AnimationSpec Defines how an animation runs. 1. Transitions ◦ Define

    the general path to follow (target state) ▪ Ej: slide to top left 2. AnimationSpec ◦ Define how to follow that path ▪ Ej: Go straight to the point ▪ Ej: First go super fast and then super slow ▪ Ej: Take a detour
  31. Crossfade var screen by remember { mutableStateOf(ScreenOne) } Crossfade( targetState

    = screen, ) { state -> when (state) { ScreenOne -> ScreenOne() ScreenTwo -> ScreenTwo() } }
  32. Crossfade var screen by remember { mutableStateOf(ScreenOne) } Crossfade( targetState

    = screen, animationSpec = tween(durationMillis = 2000), ) { state -> when (state) { ScreenOne -> ScreenOne() ScreenTwo -> ScreenTwo() } }
  33. Crossfade var screen by remember { mutableStateOf(ScreenOne) } Crossfade( targetState

    = screen, animationSpec = snap(), ) { state -> when (state) { ScreenOne -> ScreenOne() ScreenTwo -> ScreenTwo() } }
  34. Crossfade var screen by remember { mutableStateOf(ScreenOne) } Crossfade( targetState

    = screen, animationSpec = spring(), ) { state -> when (state) { ScreenOne -> ScreenOne() ScreenTwo -> ScreenTwo() } }
  35. Animated Content @Composable fun <S> AnimatedContent( targetState: S, modifier: Modifier

    = Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { fadeIn() + scaleIn() with fadeOut() }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable() AnimatedVisibilityScope.(targetState: S) -> Unit ) { val transition = updateTransition(targetState = targetState) transition.AnimatedContent( ... ) }
  36. Animated Content @Composable fun <S> AnimatedContent( targetState: S, modifier: Modifier

    = Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { fadeIn() + scaleIn() with fadeOut() }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable() AnimatedVisibilityScope.(targetState: S) -> Unit ) { val transition = updateTransition(targetState = targetState) transition.AnimatedContent( ... ) }
  37. Animated Content var isVisible by remember { mutableStateOf(true) } AnimatedContent(

    targetState = isVisible, ) { state -> if (state) { ColorScreen( ... ){ ... } } }
  38. Animated Content var isVisible by remember { mutableStateOf(true) } AnimatedContent(

    targetState = isVisible, ) { state -> if (state) { ColorScreen( ... ){ ... } } }
  39. Transitions - Enter & Exit @Composable fun <S> AnimatedContent( targetState:

    S, modifier: Modifier = Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { fadeIn() + scaleIn() with fadeOut() }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable() AnimatedVisibilityScope.(targetState: S) -> Unit ) { val transition = updateTransition(targetState = targetState) transition.AnimatedContent( ... ) }
  40. Transitions - Enter & Exit @Composable fun <S> AnimatedContent( targetState:

    S, modifier: Modifier = Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { fadeIn() + scaleIn() with fadeOut() }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable() AnimatedVisibilityScope.(targetState: S) -> Unit ) { val transition = updateTransition(targetState = targetState) transition.AnimatedContent( ... ) }
  41. Transitions - Enter & Exit @Composable fun <S> AnimatedContent( targetState:

    S, modifier: Modifier = Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { fadeIn() + scaleIn() with fadeOut() }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable() AnimatedVisibilityScope.(targetState: S) -> Unit ) { val transition = updateTransition(targetState = targetState) transition.AnimatedContent( ... ) }
  42. Transitions - Enter & Exit @Composable fun <S> AnimatedContent( targetState:

    S, modifier: Modifier = Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { enterTransition() with fadeOut() }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable() AnimatedVisibilityScope.(targetState: S) -> Unit ) { val transition = updateTransition(targetState = targetState) transition.AnimatedContent( ... ) }
  43. Transitions - Enter & Exit @Composable fun <S> AnimatedContent( targetState:

    S, modifier: Modifier = Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { enterTransition() with exitTransition() }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable() AnimatedVisibilityScope.(targetState: S) -> Unit ) { val transition = updateTransition(targetState = targetState) transition.AnimatedContent( ... ) }
  44. Transitions - Enter & Exit var isVisible by remember {

    mutableStateOf(true) } AnimatedContent( targetState = isVisible, ) { state -> if (state) { ColorScreen( ... ){ ... } } }
  45. Transitions - Enter & Exit var isVisible by remember {

    mutableStateOf(true) } AnimatedContent( targetState = isVisible, transitionSpec = { fadeIn() + expandVertically() with fadeOut() + shrinkVertically() }, ) { state -> if (state) { ColorScreen( ... ){ ... } } }
  46. Performer 1: The Curtain! • State: Visibility Boolean • Fade

    & expand transitions • It good curtain
  47. Performer 2: The Clicker var isVisible by remember { mutableStateOf(true)

    } AnimatedContent( targetState = isVisible, ) { ... }
  48. Performer 2: The Clicker var isVisible by remember { mutableStateOf(true)

    } AnimatedContent( targetState = isVisible, ) { ... }
  49. Performer 2: The Clicker var number by remember { mutableStateOf(0)

    } AnimatedContent( targetState = number, ) { ... }
  50. Performer 2: The Clicker var number by remember { mutableStateOf(0)

    } AnimatedContent( targetState = number, ) { state -> Text(text = "$state") }
  51. Performer 2: The Clicker var number by remember { mutableStateOf(0)

    } AnimatedContent( targetState = number, ) { state -> Text(text = "$state") }
  52. Performer 2: The Clicker var number by remember { mutableStateOf(0)

    } AnimatedContent( targetState = number, ) { state -> Text(text = "$state").background(...) }
  53. Performer 2: The Clicker var number by remember { mutableStateOf(0)

    } AnimatedContent( targetState = number, transitionSpec = { slideIn { IntOffset(it.width / 2, it.height / 2) } with slideOut { IntOffset(-it.width, -it.height) } }, ) { state -> Text(text = "$state").background(...) }
  54. Performer 2: The Clicker var number by remember { mutableStateOf(0)

    } AnimatedContent( targetState = number, transitionSpec = { slideIn { IntOffset(it.width / 2, it.height / 2) } with slideOut { IntOffset(-it.width, -it.height) } using SizeTransform(clip = false) }, ) { state -> Text(text = "$state").background(...) }
  55. Performer 2: The Clicker var number by remember { mutableStateOf(0)

    } AnimatedContent( targetState = number, transitionSpec = { slideIn { IntOffset(it.width / 2, it.height / 2) } with slideOut { IntOffset(-it.width, -it.height) } using SizeTransform(clip = false) }, ) { state -> Text(text = "$state").background(...) }
  56. Performer 2: The Clicker! • State: counter Int • Slide

    transition • SizeTransform to remove clipping
  57. SizeTransform Defines how a size change happens. • Enter &

    Exit transitions take care of visibility • SizeTransform takes care of size changes
  58. SizeTransform fun SizeTransform( clip: Boolean = true, sizeAnimationSpec: (initialSize: IntSize,

    targetSize: IntSize) -> FiniteAnimationSpec<IntSize> = { _, _ -> spring(visibilityThreshold = IntSize.VisibilityThreshold) } ): SizeTransform = SizeTransformImpl(clip, sizeAnimationSpec)
  59. SizeTransform fun SizeTransform( clip: Boolean = true, sizeAnimationSpec: (initialSize: IntSize,

    targetSize: IntSize) -> FiniteAnimationSpec<IntSize> = { _, _ -> spring(visibilityThreshold = IntSize.VisibilityThreshold) } ): SizeTransform = SizeTransformImpl(clip, sizeAnimationSpec)
  60. SizeTransform fun SizeTransform( clip: Boolean = true, sizeAnimationSpec: (initialSize: IntSize,

    targetSize: IntSize) -> FiniteAnimationSpec<IntSize> = { _, _ -> spring(visibilityThreshold = IntSize.VisibilityThreshold) } ): SizeTransform = SizeTransformImpl(clip, sizeAnimationSpec)
  61. SizeTransform fun SizeTransform( clip: Boolean = true, sizeAnimationSpec: (initialSize: IntSize,

    targetSize: IntSize) -> FiniteAnimationSpec<IntSize> = { _, _ -> spring(visibilityThreshold = IntSize.VisibilityThreshold) } ): SizeTransform = SizeTransformImpl(clip, sizeAnimationSpec)
  62. SizeTransform fun SizeTransform( clip: Boolean = true, sizeAnimationSpec: (initialSize: IntSize,

    targetSize: IntSize) -> FiniteAnimationSpec<IntSize> = { _, _ -> spring(visibilityThreshold = IntSize.VisibilityThreshold) } ): SizeTransform = SizeTransformImpl(clip, sizeAnimationSpec)
  63. Performer 3: The Size Shifter enum class ScreenMode { FullScreen,

    Quarter, } var screenMode by remember { mutableStateOf(FullScreen) }
  64. Performer 3: The Size Shifter var screenMode by remember {

    mutableStateOf(FullScreen) } AnimatedContent( targetState = screenMode, ) { state -> when (state) { FullScreen -> FullScreen() Quarter -> Quarter() } }
  65. Performer 3: The Size Shifter var screenMode by remember {

    mutableStateOf(FullScreen) } AnimatedContent( targetState = screenMode, ) { state -> when (state) { FullScreen -> FullScreen() Quarter -> Quarter() } }
  66. Performer 3: The Size Shifter var screenMode by remember {

    mutableStateOf(FullScreen) } AnimatedContent( targetState = screenMode, transitionSpec = {...}, ) { state -> when (state) { FullScreen -> FullScreen() Quarter -> Quarter() } }
  67. Performer 3: The Size Shifter var screenMode by remember {

    mutableStateOf(FullScreen) } AnimatedContent( targetState = screenMode, transitionSpec = {...}, ) { state -> when (state) { FullScreen -> FullScreen() Quarter -> Quarter() } }
  68. Performer 3: The Size Shifter transitionSpec = { fadeIn(tween(300, 300))

    with fadeOut(tween(600)) using SizeTransform { initialSize, targetSize ->
  69. Performer 3: The Size Shifter transitionSpec = { fadeIn(tween(300, 300))

    with fadeOut(tween(600)) using SizeTransform { initialSize, targetSize -> if (targetState == FullScreen) { keyframes { IntSize(targetSize.width, initialSize.height) at 300 durationMillis = 600 } } else { keyframes { IntSize(initialSize.width, targetSize.height) at 300 durationMillis = 600 } } } },
  70. Performer 3: The Size Shifter transitionSpec = { fadeIn(tween(300, 300))

    with fadeOut(tween(600)) using SizeTransform { initialSize, targetSize -> if (targetState == FullScreen) { keyframes { IntSize(targetSize.width, initialSize.height) at 300 durationMillis = 600 } } else { keyframes { IntSize(initialSize.width, targetSize.height) at 300 durationMillis = 600 } } } },
  71. Performer 3: The Size Shifter transitionSpec = { fadeIn(tween(300, 300))

    with fadeOut(tween(600)) using SizeTransform { initialSize, targetSize -> if (targetState == FullScreen) { // growing keyframes { IntSize(targetSize.width, initialSize.height) at 300 durationMillis = 600 } } else { // shrinking keyframes { IntSize(initialSize.width, targetSize.height) at 300 durationMillis = 600 } } } },
  72. Performer 3: The Size Shifter transitionSpec = { fadeIn(tween(300, 300))

    with fadeOut(tween(600)) using SizeTransform { initialSize, targetSize -> if (targetState == FullScreen) { keyframes { IntSize(targetSize.width, initialSize.height) at 300 durationMillis = 600 } } else { keyframes { IntSize(initialSize.width, targetSize.height) at 300 durationMillis = 600 } } } },
  73. Performer 3: The Size Shifter transitionSpec = { fadeIn(tween(300, 300))

    with fadeOut(tween(600)) using SizeTransform { initialSize, targetSize -> if (targetState == FullScreen) { keyframes { IntSize(targetSize.width, initialSize.height) at 300 durationMillis = 600 } } else { keyframes { IntSize(initialSize.width, targetSize.height) at 300 durationMillis = 600 } } } },
  74. Performer 3: The Size Shifter transitionSpec = { fadeIn(tween(300, 300))

    with fadeOut(tween(600)) using SizeTransform { initialSize, targetSize -> if (targetState == FullScreen) { keyframes { IntSize(targetSize.width, initialSize.height) at 300 durationMillis = 600 } } else { keyframes { IntSize(initialSize.width, targetSize.height) at 300 durationMillis = 600 } } } },
  75. Performer 3: The Size Shifter transitionSpec = { fadeIn(tween(300, 300))

    with fadeOut(tween(600)) using SizeTransform { initialSize, targetSize -> if (targetState == FullScreen) { keyframes { IntSize(targetSize.width, initialSize.height) at 300 durationMillis = 600 } } else { keyframes { IntSize(initialSize.width, targetSize.height) at 300 durationMillis = 600 } } } },
  76. Performer 3: The Size Shifter transitionSpec = { fadeIn(tween(300, 300))

    with fadeOut(tween(600)) using SizeTransform { initialSize, targetSize -> if (targetState == FullScreen) { keyframes { IntSize(targetSize.width, initialSize.height) at 300 durationMillis = 600 } } else { keyframes { IntSize(initialSize.width, targetSize.height) at 300 durationMillis = 600 } } } },
  77. Performer 3: The Size Shifter var screenMode by remember {

    mutableStateOf(FullScreen) } AnimatedContent( targetState = screenMode, transitionSpec = {...}, ) { state -> when (state) { FullScreen -> FullScreen() Quarter -> Quarter() } }
  78. Performer 3: The Size Shifter enum class ScreenMode { FullScreen,

    Quarter, HalfVertical, HalfHorizontal, } var screenMode by remember { mutableStateOf(FullScreen) } AnimatedContent( targetState = screenMode, transitionSpec = {...}, ) { state -> when (state) { FullScreen -> FullScreen() Quarter -> Quarter() } }
  79. Performer 3: The Size Shifter! • State: enum class •

    fade transition • Custom SizeTransform
  80. Wanna learn more? • Play with the repo: https://github.com/nicole-terc/animationsShow •

    Animations in Compose: https://developer.android.com/jetpack/compose/animation • Animations API in Jetpack Compose Video: https://youtu.be/Z_T1bVjhMLk