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.rememberCoroutineScope
31 import androidx.compose.ui.Alignment
32 import androidx.compose.ui.Modifier
33 import androidx.compose.ui.graphics.Color
34 import androidx.compose.ui.unit.dp
35 import com.android.compose.animation.scene.ContentScope
36 import com.android.compose.animation.scene.ElementKey
37 import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
38 import com.android.compose.animation.scene.SceneKey
39 import com.android.compose.animation.scene.SceneTransitionLayout
40 import com.android.compose.animation.scene.rememberMutableSceneTransitionLayoutState
41 import com.android.compose.animation.scene.transitions
42
43 object ParentSTL {
44 object Scenes {
45 val Left = SceneKey("Left")
46 val Right = SceneKey("Right")
47 }
48 }
49
50 object ChildSTL {
51 object Scenes {
52 val Top = SceneKey("Top")
53 val Bottom = SceneKey("Bottom")
54 }
55 }
56
57 object Elements {
58 val Shared = ElementKey("Shared")
59 val NotShared = ElementKey("NotShared")
60 }
61
62 @Composable
NestedSharedElementDemonull63 fun NestedSharedElementDemo(modifier: Modifier = Modifier) {
64 Column(modifier) {
65 val state =
66 rememberMutableSceneTransitionLayoutState(
67 ParentSTL.Scenes.Left,
68 transitions {
69 from(ParentSTL.Scenes.Left, to = ParentSTL.Scenes.Right) {
70 spec = tween(1500)
71 translate(Elements.NotShared, y = (-100).dp)
72 fade(Elements.NotShared)
73 scaleSize(Elements.NotShared, 0.5f, 0.5f)
74 }
75 },
76 )
77 val childState =
78 rememberMutableSceneTransitionLayoutState(
79 ChildSTL.Scenes.Top,
80 transitions {
81 from(ChildSTL.Scenes.Top, to = ChildSTL.Scenes.Bottom) {
82 spec = tween(1500)
83 translate(Elements.NotShared, x = 100.dp)
84 fade(Elements.NotShared)
85 scaleSize(Elements.NotShared, 0.5f, 0.5f)
86 }
87 },
88 )
89 val scope = rememberCoroutineScope()
90 SceneTransitionLayout(
91 state,
92 Modifier.padding(16.dp)
93 .border(3.dp, Color.Blue)
94 .clickable {
95 val targetScene =
96 when (state.currentScene) {
97 ParentSTL.Scenes.Left -> ParentSTL.Scenes.Right
98 else -> ParentSTL.Scenes.Left
99 }
100 state.setTargetScene(targetScene, scope)
101 }
102 .padding(16.dp),
103 ) {
104 scene(ParentSTL.Scenes.Left) {
105 Box(Modifier.fillMaxSize()) {
106 ChildSTL(
107 childState,
108 Modifier.align(Alignment.Center).fillMaxSize(fraction = 0.5f),
109 )
110 }
111 }
112 scene(ParentSTL.Scenes.Right) {
113 Box(Modifier.fillMaxSize()) {
114 SharedElement(Modifier.size(30.dp).align(Alignment.TopEnd))
115 }
116 }
117 }
118 }
119 }
120
121 @Composable
ContentScopenull122 private fun ContentScope.ChildSTL(
123 state: MutableSceneTransitionLayoutState,
124 modifier: Modifier = Modifier,
125 ) {
126 val scope = rememberCoroutineScope()
127 NestedSceneTransitionLayout(
128 state,
129 modifier.border(3.dp, Color.Red).clickable {
130 val targetScene =
131 when (state.currentScene) {
132 ChildSTL.Scenes.Top -> ChildSTL.Scenes.Bottom
133 else -> ChildSTL.Scenes.Top
134 }
135 state.setTargetScene(targetScene, scope)
136 },
137 ) {
138 scene(ChildSTL.Scenes.Top) {
139 Box(Modifier.fillMaxSize()) {
140 Box(
141 Modifier.align(Alignment.TopEnd)
142 .element(Elements.NotShared)
143 .size(80.dp)
144 .background(Color.Blue)
145 )
146 SharedElement(Modifier.size(100.dp))
147 }
148 }
149 scene(ChildSTL.Scenes.Bottom) {
150 Box(Modifier.fillMaxSize()) {
151 SharedElement(Modifier.align(Alignment.BottomStart).size(60.dp))
152 }
153 }
154 }
155 }
156
157 @Composable
ContentScopenull158 private fun ContentScope.SharedElement(modifier: Modifier = Modifier) {
159 Box(modifier.element(Elements.Shared).background(Color.Green, CircleShape))
160 }
161