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

今こそ始める Shared Element Transition

Avatar for oidy oidy
August 09, 2024

今こそ始める Shared Element Transition

Avatar for oidy

oidy

August 09, 2024
Tweet

More Decks by oidy

Other Decks in Programming

Transcript

  1. ©MIXI 2 Android / iOS / Flutter Android 2014 @oidy

    & https://speakerdeck.com/mixi_engineers/2024-new-grad-training-android
  2. ©MIXI Compose Animation 2022 6 : 1.3.0-alpha01 LookaheadLayout 2023 3

    : 1.5.0-alpha01 LookaheadLayout LookaheadScope 11 Transition
  3. ©MIXI Compose Animation 2022 6 : 1.3.0-alpha01 LookaheadLayout 2023 3

    : 1.5.0-alpha01 LookaheadLayout LookaheadScope 2024 4 : 1.7.0-alpha07 Shared Element Transition API 12 LookaheadScope
  4. ©MIXI ver. 15 Column { var checked by remember {

    mutableStateOf(false) } Switch( checked = checked, onCheckedChange = { checked = it }, ) if (!checked) { Row { Box(...) // • Box(...) // ▪ } } else { Column { Box(...) // • Box(...) // ▪ } } }
  5. ©MIXI 16 Column { var checked by remember { mutableStateOf(false)

    } Switch( checked = checked, onCheckedChange = { checked = it }, ) if (!checked) { Row { Box(...) // key = "circle" Box(...) // key = "rectangle" } } else { Column { Box(...) // key = "circle" Box(...) // key = "rectangle" } } } Shared Element key
  6. ©MIXI key 17 Box( Modifier .sharedElement( state = rememberSharedContentState(key =

    "circle"), animatedVisibilityScope = animatedVisibilityScope, ) .clip(CircleShape) )
  7. ©MIXI key 18 Box( Modifier .sharedElement( state = rememberSharedContentState(key =

    "circle"), animatedVisibilityScope = animatedVisibilityScope, ) .clip(CircleShape) ) Unresolved reference Unresolved reference
  8. ©MIXI 2 Scope 19 SharedTransitionLayout { AnimatedContent(targetState = checked) {

    targetState -> if (!targetState) { Box( Modifier .sharedElement( state = rememberSharedContentState(key = "circle"), animatedVisibilityScope = this@AnimatedContent, ) ... ) } else { ... } } }
  9. ©MIXI 2 Scope 20 SharedTransitionLayout { AnimatedContent(targetState = checked) {

    targetState -> if (!targetState) { Box( Modifier .sharedElement( state = rememberSharedContentState(key = "circle"), animatedVisibilityScope = this@AnimatedContent, ) ... ) } else { ... } } } SharedTransitionScope SharedTransitionScope
  10. ©MIXI 2 Scope 21 SharedTransitionLayout { AnimatedContent(targetState = checked) {

    targetState -> if (!targetState) { Box( Modifier .sharedElement( state = rememberSharedContentState(key = "circle"), animatedVisibilityScope = this@AnimatedContent, ) ... ) } else { ... } } } AnimatedVisibilityScope
  11. ©MIXI 22 if (!targetState) { Row { Box(Modifier.sharedElement(...)...) Box(Modifier.sharedElement(...)...) }

    } else { Column { Box(Modifier.sharedElement(...)...) Box(Modifier.sharedElement(...)...) } }
  12. ©MIXI Composable 23 ... if (!targetState) { StartContent() } else

    { EndContent() } } @Composable StartContent() { ... } @Composable EndContent() { ... }
  13. ©MIXI Composable 24 ... if (!targetState) { StartContent() } else

    { EndContent() } } @Composable StartContent() { ... } @Composable EndContent() { ... } 2 Scope l l CompositionLocal l Context Receiver
  14. ©MIXI Scope 25 if (!targetState) { StartContent( sharedTransitionScope = this@SharedTransitionLayout,

    animatedVisibilityScope = this@AnimatedContent, ) } else { EndContent( sharedTransitionScope = this@SharedTransitionLayout, animatedVisibilityScope = this@AnimatedContent, ) } @Composable fun StartContent( sharedTransitionScope: SharedTransitionScope, animatedVisibilityScope: AnimatedVisibilityScope, )
  15. ©MIXI CompositionLocal Scope 26 SharedTransitionLayout { CompositionLocalProvider(LocalSharedTransitionScope provides this) {

    AnimatedContent(targetState = checked) { targetState -> CompositionLocalProvider(LocalAnimatedVisibilityScope provides this) { if (!targetState) { StartContent() } else { EndContent() } } } } } @Composable fun StartContent() { val sharedTransitionScope = LocalSharedTransitionScope.current!! val animatedVisibilityScope = LocalAnimatedVisibilityScope.current!! CompositionLocal Scope Scope
  16. ©MIXI CompositionLocal Scope 27 SharedTransitionLayout { CompositionLocalProvider(LocalSharedTransitionScope provides this) {

    AnimatedContent(targetState = checked) { targetState -> CompositionLocalProvider(LocalAnimatedVisibilityScope provides this) { if (!targetState) { StartContent() } else { EndContent() } } } } } @Composable fun StartContent() { val sharedTransitionScope = LocalSharedTransitionScope.current!! val animatedVisibilityScope = LocalAnimatedVisibilityScope.current!!
  17. ©MIXI CompositionLocal Scope 28 @Composable fun Modifier.trySharedElement(key: String): Modifier {

    val sharedTransitionScope = LocalSharedTransitionScope.current val animatedVisibilityScope = LocalAnimatedVisibilityScope.current if (sharedTransitionScope == null || animatedVisibilityScope == null) { // return this } return with(sharedTransitionScope) { sharedElement( state = rememberSharedContentState(key), animatedVisibilityScope = animatedVisibilityScope ) } }
  18. ©MIXI CompositionLocal Scope 29 @Composable fun Modifier.trySharedElement(key: String): Modifier {

    val sharedTransitionScope = LocalSharedTransitionScope.current val animatedVisibilityScope = LocalAnimatedVisibilityScope.current if (sharedTransitionScope == null || animatedVisibilityScope == null) { // return this } return with(sharedTransitionScope) { sharedElement( state = rememberSharedContentState(key), animatedVisibilityScope = animatedVisibilityScope ) } } : Deep Link Shared Element
  19. ©MIXI Context Receiver Scope 30 context(SharedTransitionScope, AnimatedVisibilityScope) @Composable fun StartContent()

    { ... Modifier .sharedBounds( animatedVisibilityScope = this@AnimatedVisibilityScope, ... } SharedTransitionScope AnimatedVisibilityScope StartContent()
  20. ©MIXI l Compose Animation 1.7.0-alpha07 l SharedTransitionLayout AnimatedContent UI l

    SharedTransitionScope AnimatedVisibilityScope Modifier.sharedElement() l Shared Element key 31 Compose Shared Element Transition
  21. ©MIXI Navigation Compose 33 val navController = rememberNavController() NavHost( navController

    = navController, startDestination = Start, ) { composable<Start> { StartContent(onClick = { navController.navigate(End) }) } composable<End> { EndContent() } }
  22. ©MIXI Navigation Compose 34 val navController = rememberNavController() NavHost( navController

    = navController, startDestination = Start, ) { composable<Start> { StartContent(onClick = { navController.navigate(End) }) } composable<End> { EndContent() } } Navigation 2.8.0 type safe route @Serializable object Start @Serializable object End
  23. ©MIXI Navigation Compose 35 val navController = rememberNavController() NavHost( navController

    = navController, startDestination = Start, ) { composable<Start> { StartContent(onClick = { navController.navigate(End) }) } composable<End> { EndContent() } } AnimatedVisibilityScope
  24. ©MIXI SharedTransitionScope OK 36 val navController = rememberNavController() SharedTransitionLayout {

    NavHost( navController = navController, startDestination = Start, ) { composable<Start> { StartContent(onClick = { navController.navigate(End) }) } composable<End> { EndContent() } } }
  25. ©MIXI TopAppBar( modifier = Modifier .renderInSharedTransitionScopeOverlay( zIndexInOverlay = 1f )

    .animateEnterExit( enter = ..., exit = ..., ), ... ) UI 45 TopAppBar ( )
  26. ©MIXI Shared Element Transition l Compose Animation 1.7.0 46 :

    l Scope l Deep Link beta l Navigation Compose l or