js-bhavyansh / Shared_Element_Transition

Implemented shared element transitions with Compose Animation, type-safe navigation, and Coil for fetching image from internet.
0 stars 0 forks source link
android animation coil coil-image-loader compose-animation jetpack-compose kotlin-serialization shared-element-transition

Shared Element Transition with Jetpack Compose

This project demonstrates how to implement shared element transitions using Jetpack Compose Animation and type-safe navigation with serialization. It also utilizes Coil to fetch images from the internet through URLs.

Features

Screenshots

First Screenshot

Installation

  1. Clone the repository:

    git clone https://github.com/Bhavyansh03-tech/Shared_Element_Transition.git
  2. Open the project in Android Studio.

  3. Sync the project with Gradle files.

Usage

Dependencies

Add the following dependencies in your libs.versions.toml file:

[versions]
# COIL COMPOSE :->
coil = "2.6.0"
# ANIMATION :->
animation = "1.7.0-beta05"
foundation = "1.7.0-beta05"
# NAVIGATION :->
navigationCompose = "2.8.0-beta05"
kotlinxSerializationJson = "1.7.1"

[libraries]
# ANIMATION :->
androidx-animation = { module = "androidx.compose.animation:animation", version.ref = "animation" }
androidx-animation-core = { module = "androidx.compose.animation:animation-core", version.ref = "animation" }
androidx-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "foundation" }
# NAVIGATION :->
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
# COIL COMPOSE :->
coil = { module = "io.coil-kt:coil-compose", version.ref = "coil" }

[plugins]
# COMPOSE COMPILER :->
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
# NAVIGATION
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

Kotlin Serialization

To use Kotlin serialization, first, annotate your data class with @Serializable:

import kotlinx.serialization.Serializable

@Serializable object Home
@Serializable data class Detail(
    val imageIdArg: Int = 1
)

Then, pass the serialized data through the navigation:

composable<Detail> {
        val args = it.toRoute<Detail>()
        DetailScreen(
              animatedVisibilityScope = this@composable,
              imageId = args.imageIdArg, // Passing Image Id As Argument.
              onClick = { navController.popBackStack() }  // Going back to previous activity by removing it from back stack.
        )
}

Shared Element Transition

Shared element transitions are implemented for both images and text. Below is an example of how to achieve this:

@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun SharedTransitionScope.DetailScreen(
    animatedVisibilityScope: AnimatedVisibilityScope,
    imageId: Int,
    onClick: () -> Unit
) {
    Column(modifier = Modifier.fillMaxSize()) {
        AsyncImage(
            modifier = Modifier
                .sharedElement(
                    state = rememberSharedContentState(key = "image-${imageId}"),
                    animatedVisibilityScope = animatedVisibilityScope
                )
                .fillMaxWidth()
                .height(350.dp)
                .clickable { onClick() },
            model = imageList[imageId - 1].photo,
            contentDescription = null,
            contentScale = ContentScale.Crop
        )

        Box(
            modifier = Modifier
                .fillMaxSize()
                .padding(all = 12.dp)
        ) {
            Text(
                modifier = Modifier
                    .sharedBounds(
                        sharedContentState = rememberSharedContentState(key = "text-${imageId}"),
                        animatedVisibilityScope = animatedVisibilityScope
                    ),
                text = imageList[imageId - 1].title,
                fontSize = MaterialTheme.typography.titleLarge.fontSize,
                fontWeight = FontWeight.Medium
            )
        }
    }
}

Image Transition

AsyncImage(
      modifier = Modifier
          .sharedElement(
              state = rememberSharedContentState(key = "image-${imageId}"),
              animatedVisibilityScope = animatedVisibilityScope
          )
          .fillMaxWidth()
          .height(350.dp)
          .clickable { onClick() },
      model = imageList[imageId - 1].photo,
      contentDescription = null,
      contentScale = ContentScale.Crop
)

Text Transition

Text(
    modifier = Modifier
        .sharedBounds(
            sharedContentState = rememberSharedContentState(key = "text-${imageId}"),
            animatedVisibilityScope = animatedVisibilityScope
        ),
    text = imageList[imageId - 1].title,
    fontSize = MaterialTheme.typography.titleLarge.fontSize,
    fontWeight = FontWeight.Medium
)

Contributing

Contributions are welcome! Please fork the repository and submit a pull request for any improvements or bug fixes.

  1. Fork the repository.
  2. Create your feature branch (git checkout -b feature/your-feature).
  3. Commit your changes (git commit -am 'Add some feature').
  4. Push to the branch (git push origin feature/your-feature).
  5. Create a new Pull Request.

Contact

For questions or feedback, please contact @Bhavyansh03-tech.