diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index b67486e..fb93d25 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -3,15 +3,19 @@
+
+
+
+
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 466bf49..6a0ab86 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -52,6 +52,7 @@ android {
}
dependencies {
+ implementation(libs.commonmodule)
implementation(libs.cascade)
implementation(libs.cascade.compose)
implementation(libs.lottie)
@@ -65,15 +66,11 @@ dependencies {
implementation(libs.compose.material)
implementation(libs.compose.material3)
implementation(libs.navigation.compose)
-// implementation(libs.coil.kt)
+ implementation(libs.coil.kt)
implementation(libs.coil.kt.compose)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.espresso.core)
- // migrate deprecated deps
-// implementation("com.google.accompanist:accompanist-navigation-animation:0.29.1-alpha")
- implementation(libs.commonmodule)
- implementation("com.google.accompanist:accompanist-systemuicontroller:0.29.1-alpha")
androidTestImplementation(platform(libs.compose.bom))
androidTestImplementation(libs.android.test.compose.ui.test.junit4)
debugImplementation(libs.compose.ui.tooling)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e3cad18..4baeb81 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,12 +3,12 @@
xmlns:tools="http://schemas.android.com/tools">
+
-
-
\ No newline at end of file
diff --git a/app/src/main/java/pro/jayeshseth/animations/navigation/NavGraph.kt b/app/src/main/java/pro/jayeshseth/animations/navigation/NavGraph.kt
index 153f199..6c1c73b 100644
--- a/app/src/main/java/pro/jayeshseth/animations/navigation/NavGraph.kt
+++ b/app/src/main/java/pro/jayeshseth/animations/navigation/NavGraph.kt
@@ -10,7 +10,10 @@ import pro.jayeshseth.animations.ui.screens.AnimatedTransition
import pro.jayeshseth.animations.ui.screens.BouncyRope
import pro.jayeshseth.animations.ui.screens.HomeScreen
import pro.jayeshseth.animations.ui.screens.InfiniteRotation
+import pro.jayeshseth.animations.ui.screens.ItemPlacementAnimation
+import pro.jayeshseth.animations.ui.screens.SlideItemPlacement
import pro.jayeshseth.animations.ui.screens.SwipeRefresh
+import pro.jayeshseth.animations.ui.screens.TrippyBlinders
import pro.jayeshseth.animations.ui.screens.VisibilityAnimation
@Composable
@@ -29,7 +32,8 @@ fun NavGraph() {
navToAnimateInfiniteRotation = { navController.navigate("INFINITE_ROTATION") },
navToSwipeRefresh = { navController.navigate("SWIPE_REFRESH") },
navToBouncyRopes = { navController.navigate("BOUNCY_ROPE") },
- navToAnimateValueAsState = { navController.navigate("ANIMATE_VALUE_AS_STATE") }
+ navToAnimateValueAsState = { navController.navigate("ANIMATE_VALUE_AS_STATE") },
+ navToAnimatedListItemPlacement = { navController.navigate("ITEM_PLACEMENT_ANIMATION") }
)
}
composable("ANIMATE_VISIBILITY") {
@@ -56,5 +60,17 @@ fun NavGraph() {
composable("ANIMATE_VALUE_AS_STATE") {
AnimateValueAsState()
}
+ composable("SLIDE_IN_OUT") {
+ SlideItemPlacement()
+ }
+ composable("ITEM_PLACEMENT_ANIMATION") {
+ ItemPlacementAnimation(
+ navToTrippyBlinders = { navController.navigate("TRIPPY_BLINDER") },
+ navToSlideInOut = { navController.navigate("SLIDE_IN_OUT") }
+ )
+ }
+ composable("TRIPPY_BLINDER") {
+ TrippyBlinders()
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/pro/jayeshseth/animations/ui/composables/ControllerTemplate.kt b/app/src/main/java/pro/jayeshseth/animations/ui/composables/ControllerTemplate.kt
new file mode 100644
index 0000000..3e946b2
--- /dev/null
+++ b/app/src/main/java/pro/jayeshseth/animations/ui/composables/ControllerTemplate.kt
@@ -0,0 +1,32 @@
+package pro.jayeshseth.animations.ui.composables
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun ControllerTemplate(
+ title: @Composable () -> Unit,
+ content: @Composable () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier
+ .fillMaxWidth()
+ ) {
+ title()
+ }
+ content()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pro/jayeshseth/animations/ui/composables/DropDownTemplate.kt b/app/src/main/java/pro/jayeshseth/animations/ui/composables/DropDownTemplate.kt
new file mode 100644
index 0000000..4a4555f
--- /dev/null
+++ b/app/src/main/java/pro/jayeshseth/animations/ui/composables/DropDownTemplate.kt
@@ -0,0 +1,64 @@
+package pro.jayeshseth.animations.ui.composables
+
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuBox
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.MenuAnchorType
+import androidx.compose.material3.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun DropDownTemplate(
+ value: String,
+ expanded: Boolean,
+ onExpandedChange: (Boolean) -> Unit,
+ title: @Composable () -> Unit,
+ onDismissRequest: () -> Unit,
+ content: @Composable ColumnScope.() -> Unit,
+ modifier: Modifier = Modifier
+) {
+ ControllerTemplate(
+ modifier = modifier,
+ title = title,
+ content = {
+ ExposedDropdownMenuBox(
+ expanded = expanded,
+ onExpandedChange = onExpandedChange,
+ ) {
+ TextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .menuAnchor(MenuAnchorType.PrimaryNotEditable),
+ value = value,
+ onValueChange = {},
+ readOnly = true,
+ singleLine = true,
+ shape = RoundedCornerShape(50.dp),
+ trailingIcon = {
+ ExposedDropdownMenuDefaults.TrailingIcon(
+ expanded = expanded
+ )
+ },
+ colors = ExposedDropdownMenuDefaults.textFieldColors(
+ focusedIndicatorColor = Color.Transparent,
+ unfocusedIndicatorColor = Color.Transparent,
+ disabledIndicatorColor = Color.Transparent,
+ ),
+ )
+ ExposedDropdownMenu(
+ expanded = expanded,
+ onDismissRequest = onDismissRequest,
+ shape = RoundedCornerShape(25.dp),
+ content = content,
+ )
+ }
+ }
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/pro/jayeshseth/animations/ui/composables/SliderTemplate.kt b/app/src/main/java/pro/jayeshseth/animations/ui/composables/SliderTemplate.kt
new file mode 100644
index 0000000..8a007e1
--- /dev/null
+++ b/app/src/main/java/pro/jayeshseth/animations/ui/composables/SliderTemplate.kt
@@ -0,0 +1,104 @@
+package pro.jayeshseth.animations.ui.composables
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Slider
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import kotlin.math.roundToInt
+
+@Composable
+fun SliderTemplate(
+ title: String,
+ value: Float,
+ step: Float,
+ onValueChange: (Float) -> Unit,
+ valueRange: ClosedFloatingPointRange
+) {
+ val sliderValue = rememberUpdatedState(value)
+ ControllerTemplate(
+ title = {
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier
+ .fillMaxWidth()
+ ) {
+ Text(
+ text = title,
+ color = MaterialTheme.colorScheme.onBackground,
+ )
+ CompositionLocalProvider(
+ LocalContentColor provides MaterialTheme.colorScheme.onBackground,
+ ) {
+ val snappedValue = snapSliderValue(valueRange.start, sliderValue.value, step)
+ Text(
+ text = "${snappedValue.roundToInt()}",
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ )
+ }
+ }
+ },
+ content = {
+ Slider(
+ value = sliderValue.value,
+ onValueChange = onValueChange,
+ onValueChangeFinished = { },
+ valueRange = valueRange,
+ steps = getSteps(valueRange, step),
+ modifier = Modifier
+ .padding(top = 2.dp, bottom = 12.dp)
+ .height(24.dp),
+ )
+ }
+ )
+}
+
+private fun getSteps(valueRange: ClosedFloatingPointRange, step: Float): Int {
+ if (step == 0f) return 0
+ val start = valueRange.start
+ val end = valueRange.endInclusive
+ val steps = ((end - start) / step).toInt()
+ require(start + step * steps == end) {
+ "value range must be a multiple of step"
+ }
+ return steps - 1
+}
+
+private fun snapSliderValue(start: Float, value: Float, step: Float): Float {
+ if (step == 0f) return value
+ val distance = value - start
+ val stepsFromStart = (distance / step).roundToInt()
+ val snappedDistance = stepsFromStart * step
+ return start + snappedDistance
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun Preview() {
+ val value = remember {
+ mutableFloatStateOf(0f)
+ }
+ SliderTemplate(
+ title = "title",
+ value = value.floatValue,
+ step = 0.1f,
+ onValueChange = {
+ value.floatValue = it
+ },
+ valueRange = 0f..100f
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/pro/jayeshseth/animations/ui/composables/Toggler.kt b/app/src/main/java/pro/jayeshseth/animations/ui/composables/Toggler.kt
new file mode 100644
index 0000000..c428212
--- /dev/null
+++ b/app/src/main/java/pro/jayeshseth/animations/ui/composables/Toggler.kt
@@ -0,0 +1,35 @@
+package pro.jayeshseth.animations.ui.composables
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Switch
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun Toggler(
+ title: String,
+ checked: Boolean,
+ onCheckedChanged: (Boolean) -> Unit,
+ modifier: Modifier = Modifier,
+ enabled: Boolean = true
+) {
+ Row(
+ modifier = modifier,
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(text = title)
+ Spacer(modifier = Modifier.padding(8.dp))
+ Switch(
+ checked = checked,
+ onCheckedChange = onCheckedChanged,
+ enabled = enabled
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pro/jayeshseth/animations/ui/screens/HomeScreen.kt b/app/src/main/java/pro/jayeshseth/animations/ui/screens/HomeScreen.kt
index 1968b6e..7886629 100644
--- a/app/src/main/java/pro/jayeshseth/animations/ui/screens/HomeScreen.kt
+++ b/app/src/main/java/pro/jayeshseth/animations/ui/screens/HomeScreen.kt
@@ -26,16 +26,17 @@ fun HomeScreen(
navToAnimateInfiniteRotation: () -> Unit,
navToSwipeRefresh: () -> Unit,
navToBouncyRopes: () -> Unit,
- navToAnimateValueAsState: () -> Unit
+ navToAnimateValueAsState: () -> Unit,
+ navToAnimatedListItemPlacement: () -> Unit
) {
val scrollState = rememberScrollState()
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(rememberTopAppBarState())
HomeScaffold(
- innerScrollState = scrollState,
+ verticalScrollState = scrollState,
topAppBarScrollBehavior = scrollBehavior,
title = {
Text(
- text = "Animations in Jetpack Compose",
+ text = "Animations",
fontSize = 25.sp,
fontWeight = FontWeight.Bold
)
@@ -51,6 +52,10 @@ fun HomeScreen(
text = "Animate Visibility",
onClick = navToAnimateVisibility,
)
+ InteractiveButton(
+ text = "Animated List Item Placement",
+ onClick = navToAnimatedListItemPlacement,
+ )
InteractiveButton(
text = "Animated Content",
onClick = navToAnimateContent,
@@ -79,6 +84,10 @@ fun HomeScreen(
text = "Bouncy Ropes",
onClick = navToBouncyRopes
)
+ InteractiveButton(
+ text = "About",
+ onClick = navToBouncyRopes
+ )
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/pro/jayeshseth/animations/ui/screens/ItemPlacementAnimations.kt b/app/src/main/java/pro/jayeshseth/animations/ui/screens/ItemPlacementAnimations.kt
new file mode 100644
index 0000000..b46ac64
--- /dev/null
+++ b/app/src/main/java/pro/jayeshseth/animations/ui/screens/ItemPlacementAnimations.kt
@@ -0,0 +1,40 @@
+package pro.jayeshseth.animations.ui.screens
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.systemBarsPadding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import pro.jayeshseth.commoncomponents.InteractiveButton
+import pro.jayeshseth.commoncomponents.StatusBarAwareThemedColumn
+
+@Composable
+fun ItemPlacementAnimation(
+ navToTrippyBlinders: () -> Unit,
+ navToSlideInOut: () -> Unit,
+ modifier: Modifier = Modifier
+) {
+ StatusBarAwareThemedColumn(
+ modifier = modifier
+ .verticalScroll(
+ rememberScrollState()
+ )
+ .systemBarsPadding()
+ .padding(horizontal = 20.dp)
+ ) {
+ InteractiveButton(
+ text = "Slide In/Out",
+ onClick = navToSlideInOut,
+ )
+ InteractiveButton(
+ text = "Fidget Toy",
+ onClick = navToTrippyBlinders,
+ )
+ InteractiveButton(
+ text = "Trippy Blinders",
+ onClick = navToTrippyBlinders,
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pro/jayeshseth/animations/ui/screens/SlideItemPlacement.kt b/app/src/main/java/pro/jayeshseth/animations/ui/screens/SlideItemPlacement.kt
new file mode 100644
index 0000000..751a74e
--- /dev/null
+++ b/app/src/main/java/pro/jayeshseth/animations/ui/screens/SlideItemPlacement.kt
@@ -0,0 +1,449 @@
+package pro.jayeshseth.animations.ui.screens
+
+import android.os.Build
+import android.os.VibrationEffect
+import android.os.Vibrator
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.Crossfade
+import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.Easing
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutVertically
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.material3.Card
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.SegmentedButton
+import androidx.compose.material3.SegmentedButtonDefaults
+import androidx.compose.material3.SingleChoiceSegmentedButtonRow
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.rememberTopAppBarState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableFloatStateOf
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import androidx.core.content.ContextCompat
+import pro.jayeshseth.animations.R
+import pro.jayeshseth.animations.ui.composables.DropDownTemplate
+import pro.jayeshseth.animations.ui.composables.SliderTemplate
+import pro.jayeshseth.animations.ui.composables.Toggler
+import pro.jayeshseth.animations.util.DampingRatioList
+import pro.jayeshseth.animations.util.EasingList
+import pro.jayeshseth.animations.util.StiffnessList
+import pro.jayeshseth.commoncomponents.HomeScaffold
+import pro.jayeshseth.commoncomponents.StatusBarAwareThemedLazyColumn
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SlideItemPlacement(modifier: Modifier = Modifier) {
+ var isVisible by remember { mutableStateOf(false) }
+ val scrollBehavior =
+ TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState())
+ val translateX = remember { mutableStateOf(true) }
+ val vibrationEffect = remember { mutableStateOf(true) }
+ val translateY = remember { mutableStateOf(false) }
+ val initialValue = remember { mutableFloatStateOf(300f) }
+ var selectedIndex by remember { mutableIntStateOf(0) }
+ var tweenDuration by remember { mutableIntStateOf(300) }
+ var showShadow by remember { mutableStateOf(true) }
+ val easingList = remember { EasingList }
+ var selectedEasing by remember { mutableStateOf(easingList[0]) }
+ val dampingRatioList = remember { DampingRatioList }
+ var selectedDampingRatio by remember { mutableStateOf(dampingRatioList[0]) }
+ val stiffnessList = remember { StiffnessList }
+ var selectedStiffness by remember { mutableStateOf(stiffnessList[0]) }
+
+ HomeScaffold(
+ topAppBarScrollBehavior = scrollBehavior,
+ modifier = modifier,
+ actions = {
+ IconButton(onClick = { isVisible = !isVisible }) {
+ Icon(imageVector = Icons.Default.Settings, contentDescription = null)
+ }
+ },
+ title = {
+ if (!isVisible) {
+ Text(
+ "Slide In / Out"
+ )
+ } else {
+ Text(
+ "Animation Controller"
+ )
+ }
+ }
+ ) {
+ StatusBarAwareThemedLazyColumn(
+ statusBarColor = Color.Transparent
+ ) {
+ item {
+ Spacer(modifier = Modifier.padding(top = it.calculateTopPadding()))
+ }
+ item {
+ AnimatedVisibility(
+ isVisible,
+ enter = slideInVertically(tween(500)),
+ exit = slideOutVertically(tween(500)),
+ ) {
+ AnimationController(
+ translateX = translateX.value,
+ onTranslateXChanged = { translateX.value = it },
+ translateY = translateY.value,
+ onTranslateYChanged = { translateY.value = it },
+ initialValue = initialValue.floatValue,
+ onInitialValueChange = { initialValue.floatValue = it },
+ vibrationEffect = vibrationEffect.value,
+ onVibrationEffectChanged = { vibrationEffect.value = it },
+ initialValueRange = -1000f..1000f,
+ selectedIndex = selectedIndex,
+ onAnimationSpecClick = { index -> selectedIndex = index },
+ easingList = easingList,
+ selectedEasing = selectedEasing.second,
+ onEasingClick = { selectedEasing = it },
+ tweenDuration = tweenDuration,
+ onTweenDurationChange = { tweenDuration = it },
+ showShadow = showShadow,
+ onShowShadowChanged = { showShadow = it },
+ stiffness = stiffnessList,
+ selectedStiffness = selectedStiffness.second,
+ onStiffnessClick = { selectedStiffness = it },
+ dampingRatio = dampingRatioList,
+ selectedDampingRatio = selectedDampingRatio.second,
+ onDampingRatioClick = { selectedDampingRatio = it },
+ )
+ }
+ }
+ items(100000) {
+ ListItem(
+ index = it,
+ isTranslateX = translateX.value,
+ isTranslateY = translateY.value,
+ isVibration = vibrationEffect.value,
+ initialValue = initialValue.floatValue,
+ tweenDuration = tweenDuration,
+ showShadow = showShadow,
+ easing = selectedEasing.first,
+ isTween = selectedIndex == 0,
+ stiffness = selectedStiffness.first,
+ dampingRatio = selectedDampingRatio.first,
+ )
+ }
+ }
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun AnimationController(
+ translateX: Boolean,
+ onTranslateXChanged: (Boolean) -> Unit,
+ translateY: Boolean,
+ onTranslateYChanged: (Boolean) -> Unit,
+ vibrationEffect: Boolean,
+ onVibrationEffectChanged: (Boolean) -> Unit,
+ showShadow: Boolean,
+ onShowShadowChanged: (Boolean) -> Unit,
+ initialValue: Float,
+ onInitialValueChange: (Float) -> Unit,
+ initialValueRange: ClosedFloatingPointRange,
+ tweenDuration: Int,
+ onTweenDurationChange: (Int) -> Unit,
+ selectedIndex: Int,
+ onAnimationSpecClick: (Int) -> Unit,
+ easingList: List>,
+ selectedEasing: String,
+ onEasingClick: (Pair) -> Unit,
+ dampingRatio: List>,
+ selectedDampingRatio: String,
+ onDampingRatioClick: (Pair) -> Unit,
+ stiffness: List>,
+ selectedStiffness: String,
+ onStiffnessClick: (Pair) -> Unit,
+ modifier: Modifier = Modifier
+) {
+ val animationSpecs = listOf("tween", "spring")
+ var expanded by remember { mutableStateOf(false) }
+ var dampingRatioExpanded by remember { mutableStateOf(false) }
+ var stiffnessExpanded by remember { mutableStateOf(false) }
+
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(18.dp),
+ modifier = modifier
+ .fillMaxWidth()
+ .padding(bottom = 12.dp)
+ .animateContentSize(tween(450))
+ ) {
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Toggler(
+ title = "Translate X",
+ checked = translateX,
+ onCheckedChanged = onTranslateXChanged
+ )
+ Toggler(
+ title = "Translate Y",
+ checked = translateY,
+ onCheckedChanged = onTranslateYChanged
+ )
+ }
+ Toggler(
+ title = "Vibration Effect",
+ checked = vibrationEffect,
+ onCheckedChanged = onVibrationEffectChanged,
+ modifier = Modifier.fillMaxWidth()
+ )
+ Toggler(
+ title = "Shadow",
+ checked = showShadow,
+ onCheckedChanged = onShowShadowChanged,
+ modifier = Modifier.fillMaxWidth()
+ )
+ SliderTemplate(
+ title = "Initial Value",
+ value = initialValue,
+ step = 0.1f,
+ onValueChange = onInitialValueChange,
+ valueRange = initialValueRange
+ )
+ SingleChoiceSegmentedButtonRow {
+ animationSpecs.forEachIndexed { index, label ->
+ SegmentedButton(
+ shape = SegmentedButtonDefaults.itemShape(index, animationSpecs.size),
+ selected = index == selectedIndex,
+ onClick = { onAnimationSpecClick(index) }
+ ) {
+ Text(label)
+ }
+ }
+ }
+ Crossfade(
+ targetState = selectedIndex,
+ animationSpec = tween(850, easing = LinearEasing),
+ label = "control switcher"
+ ) {
+ when (it) {
+ 0 -> {
+ Column {
+ SliderTemplate(
+ title = "Tween Duration",
+ value = tweenDuration.toFloat(),
+ step = 5f,
+ onValueChange = { duration -> onTweenDurationChange(duration.toInt()) },
+ valueRange = 0f..1000f
+ )
+
+ DropDownTemplate(
+ value = selectedEasing,
+ expanded = expanded,
+ onExpandedChange = { expanded = it },
+ onDismissRequest = { expanded = false },
+ title = {
+ Text(
+ text = "Easing",
+ color = MaterialTheme.colorScheme.onBackground,
+ )
+ },
+ content = {
+ easingList.forEach { easing ->
+ DropdownMenuItem(
+ text = {
+ Text(
+ easing.second,
+ style = MaterialTheme.typography.bodyLarge
+ )
+ },
+ onClick = {
+ onEasingClick(easing)
+ expanded = false
+ },
+ modifier = Modifier.clip(CircleShape),
+ contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
+ )
+ }
+
+ }
+ )
+ }
+ }
+
+ 1 -> {
+ Column {
+ DropDownTemplate(
+ value = selectedDampingRatio,
+ expanded = dampingRatioExpanded,
+ onExpandedChange = { dampingRatioExpanded = it },
+ onDismissRequest = { dampingRatioExpanded = false },
+ title = {
+ Text(
+ text = "Damping Ratio",
+ color = MaterialTheme.colorScheme.onBackground,
+ )
+ },
+ content = {
+ dampingRatio.forEach { ratio ->
+ DropdownMenuItem(
+ text = {
+ Text(
+ ratio.second,
+ style = MaterialTheme.typography.bodyLarge
+ )
+ },
+ onClick = {
+ onDampingRatioClick(ratio)
+ dampingRatioExpanded = false
+ },
+ modifier = Modifier.clip(CircleShape),
+ contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
+ )
+ }
+
+ }
+ )
+ DropDownTemplate(
+ value = selectedStiffness,
+ expanded = stiffnessExpanded,
+ onExpandedChange = { stiffnessExpanded = it },
+ onDismissRequest = { stiffnessExpanded = false },
+ title = {
+ Text(
+ text = "Stiffness",
+ color = MaterialTheme.colorScheme.onBackground,
+ )
+ },
+ content = {
+ stiffness.forEach {
+ DropdownMenuItem(
+ text = {
+ Text(
+ it.second,
+ style = MaterialTheme.typography.bodyLarge
+ )
+ },
+ onClick = {
+ onStiffnessClick(it)
+ stiffnessExpanded = false
+ },
+ modifier = Modifier.clip(CircleShape),
+ contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
+ )
+ }
+
+ }
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun ListItem(
+ index: Int,
+ initialValue: Float,
+ isTranslateX: Boolean,
+ isTranslateY: Boolean,
+ showShadow: Boolean,
+ isTween: Boolean,
+ isVibration: Boolean,
+ tweenDuration: Int,
+ easing: Easing,
+ dampingRatio: Float,
+ stiffness: Float,
+ modifier: Modifier = Modifier
+) {
+ val context = LocalContext.current
+ val animatedProgress = remember { Animatable(initialValue) }
+ val vibrator = ContextCompat.getSystemService(context, Vibrator::class.java)
+ val shadowColor = MaterialTheme.colorScheme.primary
+
+ LaunchedEffect(index) {
+ if (isVibration) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ vibrator!!.vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK))
+ }
+ }
+ if (isTween) {
+ animatedProgress.animateTo(
+ targetValue = 0f,
+ animationSpec = tween(tweenDuration, easing = easing)
+ )
+ } else {
+ animatedProgress.animateTo(
+ targetValue = 0f,
+ animationSpec = spring(
+ dampingRatio = dampingRatio,
+ stiffness = stiffness,
+ )
+ )
+ }
+ }
+ Card(
+ shape = RoundedCornerShape(12.dp),
+ modifier = modifier
+ .padding(8.dp)
+ .graphicsLayer {
+ shape = RoundedCornerShape(12.dp)
+ if (showShadow) shadowElevation = animatedProgress.value
+ if (showShadow) spotShadowColor = shadowColor
+ if (showShadow) ambientShadowColor = shadowColor
+ if (isTranslateX) translationX = animatedProgress.value
+ if (isTranslateY) translationY = animatedProgress.value
+ }) {
+ Row(
+ Modifier
+ .fillMaxWidth()
+ .padding(8.dp), verticalAlignment = Alignment.CenterVertically
+ ) {
+ Image(
+ painter = painterResource(R.drawable.cat),
+ contentDescription = "",
+ modifier = Modifier
+ .size(50.dp)
+ .clip(RoundedCornerShape(10.dp)),
+ contentScale = ContentScale.Crop
+ )
+ Text(
+ text = "meow",
+ modifier = Modifier.padding(horizontal = 20.dp)
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/pro/jayeshseth/animations/util/DampingRatioList.kt b/app/src/main/java/pro/jayeshseth/animations/util/DampingRatioList.kt
new file mode 100644
index 0000000..3bb967a
--- /dev/null
+++ b/app/src/main/java/pro/jayeshseth/animations/util/DampingRatioList.kt
@@ -0,0 +1,10 @@
+package pro.jayeshseth.animations.util
+
+import androidx.compose.animation.core.Spring
+
+val DampingRatioList = mutableListOf(
+ Pair(Spring.DampingRatioHighBouncy, "High Bouncy"),
+ Pair(Spring.DampingRatioMediumBouncy, "Medium Bouncy"),
+ Pair(Spring.DampingRatioLowBouncy, "Low Bouncy"),
+ Pair(Spring.DampingRatioNoBouncy, "No Bouncy"),
+)
diff --git a/app/src/main/java/pro/jayeshseth/animations/util/EasingList.kt b/app/src/main/java/pro/jayeshseth/animations/util/EasingList.kt
new file mode 100644
index 0000000..8970602
--- /dev/null
+++ b/app/src/main/java/pro/jayeshseth/animations/util/EasingList.kt
@@ -0,0 +1,77 @@
+package pro.jayeshseth.animations.util
+
+import androidx.compose.animation.core.Ease
+import androidx.compose.animation.core.EaseIn
+import androidx.compose.animation.core.EaseInBack
+import androidx.compose.animation.core.EaseInBounce
+import androidx.compose.animation.core.EaseInCirc
+import androidx.compose.animation.core.EaseInCubic
+import androidx.compose.animation.core.EaseInElastic
+import androidx.compose.animation.core.EaseInExpo
+import androidx.compose.animation.core.EaseInOutBack
+import androidx.compose.animation.core.EaseInOutBounce
+import androidx.compose.animation.core.EaseInOutCirc
+import androidx.compose.animation.core.EaseInOutCubic
+import androidx.compose.animation.core.EaseInOutElastic
+import androidx.compose.animation.core.EaseInOutExpo
+import androidx.compose.animation.core.EaseInOutQuad
+import androidx.compose.animation.core.EaseInOutQuart
+import androidx.compose.animation.core.EaseInOutQuint
+import androidx.compose.animation.core.EaseInOutSine
+import androidx.compose.animation.core.EaseInQuad
+import androidx.compose.animation.core.EaseInQuart
+import androidx.compose.animation.core.EaseInQuint
+import androidx.compose.animation.core.EaseInSine
+import androidx.compose.animation.core.EaseOut
+import androidx.compose.animation.core.EaseOutBack
+import androidx.compose.animation.core.EaseOutBounce
+import androidx.compose.animation.core.EaseOutCirc
+import androidx.compose.animation.core.EaseOutElastic
+import androidx.compose.animation.core.EaseOutExpo
+import androidx.compose.animation.core.EaseOutQuad
+import androidx.compose.animation.core.EaseOutQuart
+import androidx.compose.animation.core.EaseOutQuint
+import androidx.compose.animation.core.EaseOutSine
+import androidx.compose.animation.core.FastOutLinearInEasing
+import androidx.compose.animation.core.FastOutSlowInEasing
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.LinearOutSlowInEasing
+
+val EasingList = mutableListOf(
+ Pair(LinearEasing, "LinearEasing"),
+ Pair(FastOutSlowInEasing, "FastOutSlowInEasing"),
+ Pair(FastOutLinearInEasing, "FastOutLinearInEasing"),
+ Pair(LinearOutSlowInEasing, "LinearOutSlowInEasing"),
+ Pair(Ease, "Ease"),
+ Pair(EaseIn, "EaseIn"),
+ Pair(EaseOut, "EaseOut"),
+ Pair(EaseInSine, "EaseInSine"),
+ Pair(EaseOutSine, "EaseOutSine"),
+ Pair(EaseInOutSine, "EaseInOutSine"),
+ Pair(EaseInCubic, "EaseInCubic"),
+ Pair(EaseInOutCubic, "EaseInOutCubic"),
+ Pair(EaseInQuint, "EaseInQuint"),
+ Pair(EaseOutQuint, "EaseOutQuint"),
+ Pair(EaseInOutQuint, "EaseInOutQuint"),
+ Pair(EaseInCirc, "EaseInCirc"),
+ Pair(EaseOutCirc, "EaseOutCirc"),
+ Pair(EaseInOutCirc, "EaseInOutCirc"),
+ Pair(EaseInQuad, "EaseInQuad"),
+ Pair(EaseOutQuad, "EaseOutQuad"),
+ Pair(EaseInOutQuad, "EaseInOutQuad"),
+ Pair(EaseInQuart, "EaseInQuart"),
+ Pair(EaseOutQuart, "EaseOutQuart"),
+ Pair(EaseInOutQuart, "EaseInOutQuart"),
+ Pair(EaseInExpo, "EaseInExpo"),
+ Pair(EaseOutExpo, "EaseOutExpo"),
+ Pair(EaseInOutExpo, "EaseInOutExpo"),
+ Pair(EaseInBack, "EaseInBack"),
+ Pair(EaseOutBack, "EaseOutBack"),
+ Pair(EaseInOutBack, "EaseInOutBack"),
+ Pair(EaseInElastic, "EaseInElastic"),
+ Pair(EaseOutElastic, "EaseOutElastic"),
+ Pair(EaseInOutElastic, "EaseInOutElastic"),
+ Pair(EaseInBounce, "EaseInBounce"),
+ Pair(EaseOutBounce, "EaseOutBounce"),
+ Pair(EaseInOutBounce, "EaseInOutBounce")
+)
\ No newline at end of file
diff --git a/app/src/main/java/pro/jayeshseth/animations/util/StiffnessList.kt b/app/src/main/java/pro/jayeshseth/animations/util/StiffnessList.kt
new file mode 100644
index 0000000..26a685f
--- /dev/null
+++ b/app/src/main/java/pro/jayeshseth/animations/util/StiffnessList.kt
@@ -0,0 +1,11 @@
+package pro.jayeshseth.animations.util
+
+import androidx.compose.animation.core.Spring
+
+val StiffnessList = mutableListOf(
+ Pair(Spring.StiffnessVeryLow, "Very Low"),
+ Pair(Spring.StiffnessLow, "Low"),
+ Pair(Spring.StiffnessMedium, "Medium"),
+ Pair(Spring.StiffnessMediumLow, "Medium Low"),
+ Pair(Spring.StiffnessHigh, "High"),
+)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 4a17dbe..8324952 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -8,7 +8,7 @@ androidx-window = "1.3.0"
applicationId = "pro.jayeshseth.animations"
cascade = "2.0.0-rc02"
coil = "2.4.0"
-commonmodule = "0.0.5"
+commonmodule = "0.0.7"
compile-sdk = "34"
compose-bom = "2024.06.00"
compose-compiler = "1.5.0"
@@ -75,7 +75,7 @@ commonmodule = { module = "pro.jayeshseth.madappmodules:commonModules", version.
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
compose-material-iconsExtended = { group = "androidx.compose.material3", name = "material3" }
compose-material = { group = "androidx.compose.material", name = "material" }
-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
+compose-material3 = { group = "androidx.compose.material3", name = "material3", version = "1.3.0-beta05" }
compose-material3-window = { group = "androidx.compose.material3", name = "material3-window-size-class" }
compose-runtime = { group = "androidx.compose.runtime", name = "runtime" }
compose-ui = { group = "androidx.compose.ui", name = "ui" }