1 /*
2 * Copyright (C) 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.compose.animation.scene.demo
18
19 import androidx.compose.animation.core.tween
20 import androidx.compose.foundation.background
21 import androidx.compose.foundation.border
22 import androidx.compose.foundation.clickable
23 import androidx.compose.foundation.layout.Box
24 import androidx.compose.foundation.layout.Column
25 import androidx.compose.foundation.layout.fillMaxSize
26 import androidx.compose.foundation.layout.padding
27 import androidx.compose.foundation.layout.size
28 import androidx.compose.foundation.shape.CircleShape
29 import androidx.compose.runtime.Composable
30 import androidx.compose.runtime.remember
31 import androidx.compose.runtime.rememberCoroutineScope
32 import androidx.compose.ui.Alignment
33 import androidx.compose.ui.Modifier
34 import androidx.compose.ui.graphics.Color
35 import androidx.compose.ui.unit.dp
36 import com.android.compose.animation.scene.ContentScope
37 import com.android.compose.animation.scene.ElementKey
38 import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
39 import com.android.compose.animation.scene.SceneKey
40 import com.android.compose.animation.scene.SceneScope
41 import com.android.compose.animation.scene.SceneTransitionLayout
42 import com.android.compose.animation.scene.transitions
43
44 object ParentSTL {
45 object Scenes {
46 val Left = SceneKey("Left")
47 val Right = SceneKey("Right")
48 }
49 }
50
51 object ChildSTL {
52 object Scenes {
53 val Top = SceneKey("Red")
54 val Bottom = SceneKey("Red")
55 }
56 }
57
58 object Elements {
59 val Shared = ElementKey("Shared")
60 }
61
62 @Composable
NestedSharedElementDemonull63 fun NestedSharedElementDemo(modifier: Modifier = Modifier) {
64 Column(modifier) {
65 val state = remember {
66 MutableSceneTransitionLayoutState(
67 ParentSTL.Scenes.Left,
68 transitions {
69 from(ParentSTL.Scenes.Left, to = ParentSTL.Scenes.Right) { spec = tween(500) }
70 },
71 )
72 }
73 val childState = remember {
74 MutableSceneTransitionLayoutState(
75 ChildSTL.Scenes.Top,
76 transitions {
77 from(ChildSTL.Scenes.Top, to = ChildSTL.Scenes.Bottom) { spec = tween(500) }
78 },
79 )
80 }
81 val scope = rememberCoroutineScope()
82 SceneTransitionLayout(
83 state,
84 Modifier.padding(16.dp)
85 .border(3.dp, Color.Blue)
86 .clickable {
87 val targetScene =
88 when (state.currentScene) {
89 ParentSTL.Scenes.Left -> ParentSTL.Scenes.Right
90 else -> ParentSTL.Scenes.Left
91 }
92 state.setTargetScene(targetScene, scope)
93 }
94 .padding(16.dp),
95 ) {
96 scene(ParentSTL.Scenes.Right) {
97 Box(Modifier.fillMaxSize()) {
98 SharedElement(Modifier.size(30.dp).align(Alignment.TopEnd))
99 }
100 }
101
102 scene(ParentSTL.Scenes.Left) {
103 Box(Modifier.fillMaxSize()) {
104 ChildSTL(
105 childState,
106 Modifier.align(Alignment.Center).fillMaxSize(fraction = 0.5f),
107 )
108 }
109 }
110 }
111 }
112 }
113
114 @Composable
ContentScopenull115 private fun ContentScope.ChildSTL(
116 state: MutableSceneTransitionLayoutState,
117 modifier: Modifier = Modifier,
118 ) {
119 val scope = rememberCoroutineScope()
120 NestedSceneTransitionLayout(
121 state,
122 modifier.border(3.dp, Color.Red).clickable {
123 val targetScene =
124 when (state.currentScene) {
125 ChildSTL.Scenes.Top -> ChildSTL.Scenes.Bottom
126 else -> ChildSTL.Scenes.Top
127 }
128 state.setTargetScene(targetScene, scope)
129 },
130 ) {
131 scene(ChildSTL.Scenes.Top) {
132 Box(Modifier.fillMaxSize()) { SharedElement(Modifier.size(100.dp)) }
133 }
134 scene(ChildSTL.Scenes.Bottom) {
135 Box(Modifier.fillMaxSize()) {
136 SharedElement(Modifier.align(Alignment.BottomStart).size(60.dp))
137 }
138 }
139 }
140 }
141
142 @Composable
SharedElementnull143 private fun SceneScope.SharedElement(modifier: Modifier = Modifier) {
144 Box(modifier.element(Elements.Shared).background(Color.Green, CircleShape))
145 }
146