1 /*
<lambda>null2  * Copyright 2023 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 @file:OptIn(ExperimentalMotionApi::class)
18 
19 package androidx.constraintlayout.compose.demos
20 
21 import androidx.compose.animation.core.animateFloatAsState
22 import androidx.compose.animation.core.tween
23 import androidx.compose.foundation.background
24 import androidx.compose.foundation.layout.Arrangement
25 import androidx.compose.foundation.layout.Box
26 import androidx.compose.foundation.layout.Column
27 import androidx.compose.foundation.layout.Row
28 import androidx.compose.foundation.layout.fillMaxSize
29 import androidx.compose.foundation.layout.fillMaxWidth
30 import androidx.compose.foundation.layout.size
31 import androidx.compose.material.Button
32 import androidx.compose.material.Text
33 import androidx.compose.runtime.Composable
34 import androidx.compose.runtime.derivedStateOf
35 import androidx.compose.runtime.getValue
36 import androidx.compose.runtime.mutableStateOf
37 import androidx.compose.runtime.remember
38 import androidx.compose.runtime.setValue
39 import androidx.compose.ui.Modifier
40 import androidx.compose.ui.graphics.Color
41 import androidx.compose.ui.layout.layoutId
42 import androidx.compose.ui.tooling.preview.Preview
43 import androidx.compose.ui.unit.dp
44 import androidx.constraintlayout.compose.ChainStyle
45 import androidx.constraintlayout.compose.ExperimentalMotionApi
46 import androidx.constraintlayout.compose.MotionLayout
47 import androidx.constraintlayout.compose.MotionScene
48 
49 private const val STAGGERED_VALUE = 0.4f
50 
51 @Preview
52 @Composable
53 fun SimpleStaggeredDemo() {
54     var mode by remember { mutableStateOf(StaggeredMode.Normal) }
55     var animateToEnd by remember { mutableStateOf(false) }
56     val progress by
57         animateFloatAsState(
58             targetValue = if (animateToEnd) 1f else 0f,
59             animationSpec = tween(3000),
60         )
61     val boxesId: IntArray = remember { IntArray(10) { it } }
62     val staggeredValue by remember {
63         derivedStateOf {
64             when (mode) {
65                 StaggeredMode.Inverted -> -STAGGERED_VALUE
66                 else -> STAGGERED_VALUE
67             }
68         }
69     }
70 
71     Column(Modifier.fillMaxSize()) {
72         MotionLayout(
73             remember(mode) {
74                 MotionScene {
75                     val refs = boxesId.map { createRefFor(it) }.toTypedArray()
76                     val weights =
77                         when (mode) {
78                             StaggeredMode.Custom -> boxesId.map { it.toFloat() }.shuffled()
79                             else -> boxesId.map { Float.NaN }
80                         }
81 
82                     defaultTransition(
83                         constraintSet {
84                             createHorizontalChain(*refs, chainStyle = ChainStyle.Packed(0f))
85                             refs.forEachIndexed { index, ref ->
86                                 constrain(ref) { staggeredWeight = weights[index] }
87                             }
88                         },
89                         constraintSet {
90                             createVerticalChain(*refs, chainStyle = ChainStyle.Packed(1f))
91                             constrain(*refs) { end.linkTo(parent.end) }
92                         }
93                     ) {
94                         maxStaggerDelay = staggeredValue
95                     }
96                 }
97             },
98             progress = progress,
99             Modifier.fillMaxWidth().weight(1f, true)
100         ) {
101             for (id in boxesId) {
102                 Box(modifier = Modifier.size(25.dp).background(Color.Red).layoutId(id))
103             }
104         }
105         Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
106             Button(onClick = { animateToEnd = !animateToEnd }) { Text(text = "Run") }
107             Button(
108                 onClick = {
109                     mode =
110                         when (mode) {
111                             StaggeredMode.Normal -> StaggeredMode.Inverted
112                             StaggeredMode.Inverted -> StaggeredMode.Custom
113                             else -> StaggeredMode.Normal
114                         }
115                 }
116             ) {
117                 Text(text = "Mode: ${mode.name}, Value: $staggeredValue")
118             }
119         }
120     }
121 }
122 
123 private enum class StaggeredMode {
124     Normal,
125     Inverted,
126     Custom
127 }
128