Skip to content

Commit

Permalink
Added transition Animation for People profile screen. New transition …
Browse files Browse the repository at this point in the history
…animation is available in androidx.compose.ui since v1.7.0 alpha and navigation-compose 2.8.0 alpha.
  • Loading branch information
dmitriy-chernysh committed Apr 26, 2024
1 parent 9160955 commit 7d1bebe
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 91 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=mobiledevpro_Jetpack-Compose-ChatApp-Template&metric=alert_status)](https://sonarcloud.io/dashboard?id=mobiledevpro_Jetpack-Compose-ChatApp-Template)

[![Kotlin Version](https://img.shields.io/badge/Kotlin-1.9.23-blue.svg?style=flat-square)](http://kotlinlang.org/)
[![Compose Bom](https://img.shields.io/badge/Compose%20Bom-2024.04.00-blue.svg?style=flat-square)]([http://kotlinlang.org/](https://developer.android.com/jetpack/compose/bom/bom-mapping))
[![Compose Bom](https://img.shields.io/badge/Compose%20Bom-2024.04.01-blue.svg?style=flat-square)]([http://kotlinlang.org/](https://developer.android.com/jetpack/compose/bom/bom-mapping))
[![Gradle](https://img.shields.io/badge/Gradle-8.2.0-blue.svg?style=flat-square)](https://developer.android.com/build/releases/gradle-plugin)
[![API](https://img.shields.io/badge/Min%20SDK-24%20[Android%207.0]-blue.svg?style=flat-square)](https://github.com/AndroidSDKSources/android-sdk-sources-list)
[![Target SDK](https://img.shields.io/badge/Target%20SDK-34%20[Android%2014]-blue.svg?style=flat-square)](https://developer.android.com/about/versions/13)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ data class PeopleProfile(
val id: Int,
val name: String,
val status: Boolean,
val photo: Uri? = null
val photo: Uri? = null,
val instagram: Uri? = Uri.parse("https://instagram.com/mobiledevpro"),
val linkedin: Uri? = Uri.parse("https://www.linkedin.com/in/dmitriychernysh/"),
val youtube: Uri? = Uri.parse("https://www.youtube.com/@mobiledevpro"),
val twitter: Uri? = Uri.parse("https://twitter.com/mobiledev_pro")
) {
fun listKey(): String = "${id}_${name.replace("\\s+".toRegex(), "")}"
}
Expand All @@ -41,72 +45,73 @@ val fakePeopleProfileList = arrayListOf(
id = 0,
name = "Michaela Runnings",
status = true,
Uri.parse("https://images.unsplash.com/photo-1485290334039-a3c69043e517?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80")
photo = Uri.parse("https://images.unsplash.com/photo-1485290334039-a3c69043e517?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80")

),
PeopleProfile(
id = 1,
name = "John Pestridge",
status = false,
Uri.parse("https://images.unsplash.com/photo-1542178243-bc20204b769f?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTB8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
photo = Uri.parse("https://images.unsplash.com/photo-1542178243-bc20204b769f?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTB8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
),
PeopleProfile(
id = 2,
name = "Manilla Andrews",
status = true,
Uri.parse("https://images.unsplash.com/photo-1543123820-ac4a5f77da38?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NDh8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
photo = Uri.parse("https://images.unsplash.com/photo-1543123820-ac4a5f77da38?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NDh8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
),
PeopleProfile(
id = 3,
name = "Dan Spicer",
status = false,
Uri.parse("https://images.unsplash.com/photo-1595152772835-219674b2a8a6?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NDd8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
photo = Uri.parse("https://images.unsplash.com/photo-1595152772835-219674b2a8a6?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NDd8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
),
PeopleProfile(
id = 4,
name = "Keanu Dester",
status = false,
Uri.parse("https://images.unsplash.com/photo-1597528380214-aa94bde3fc32?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NTZ8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
photo = Uri.parse("https://images.unsplash.com/photo-1597528380214-aa94bde3fc32?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NTZ8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
),
PeopleProfile(
id = 5,
name = "Anichu Patel",
status = false,
Uri.parse("https://images.unsplash.com/photo-1598641795816-a84ac9eac40c?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NjJ8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
photo = Uri.parse("https://images.unsplash.com/photo-1598641795816-a84ac9eac40c?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NjJ8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
),
PeopleProfile(
id = 6,
name = "Kienla Onso",
status = true,
Uri.parse("https://images.unsplash.com/photo-1566895733044-d2bdda8b6234?ixid=MXwxMjA3fDB8MHxzZWFyY2h8ODh8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
photo = Uri.parse("https://images.unsplash.com/photo-1566895733044-d2bdda8b6234?ixid=MXwxMjA3fDB8MHxzZWFyY2h8ODh8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
),
PeopleProfile(
id = 7,
name = "Andra Matthews",
status = false,
Uri.parse("https://images.unsplash.com/photo-1530577197743-7adf14294584?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NTV8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
photo = Uri.parse("https://images.unsplash.com/photo-1530577197743-7adf14294584?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NTV8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
),
PeopleProfile(
id = 8,
name = "Georgia S.", status = false,
Uri.parse("https://images.unsplash.com/photo-1547212371-eb5e6a4b590c?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTA3fHxwb3J0cmFpdHxlbnwwfDJ8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
photo = Uri.parse("https://images.unsplash.com/photo-1547212371-eb5e6a4b590c?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTA3fHxwb3J0cmFpdHxlbnwwfDJ8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
),
PeopleProfile(
id = 8,
name = "Matt Dengo",
status = false,
Uri.parse("https://images.unsplash.com/photo-1578176603894-57973e38890f?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTE0fHxwb3J0cmFpdHxlbnwwfDJ8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
photo = Uri.parse("https://images.unsplash.com/photo-1578176603894-57973e38890f?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTE0fHxwb3J0cmFpdHxlbnwwfDJ8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
),
PeopleProfile(
id = 9,
name = "Marsha T.",
status = true,
Uri.parse("https://images.unsplash.com/photo-1605087880595-8cc6db61f3c6?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTI0fHxwb3J0cmFpdHxlbnwwfDJ8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
photo = Uri.parse("https://images.unsplash.com/photo-1605087880595-8cc6db61f3c6?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTI0fHxwb3J0cmFpdHxlbnwwfDJ8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
),
PeopleProfile(
id = 10,
name = "Invshu Patel",
status = true,
Uri.parse("https://images.unsplash.com/photo-1561820009-8bef03ebf8e5?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTM3fHxwb3J0cmFpdHxlbnwwfDJ8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
photo = Uri.parse("https://images.unsplash.com/photo-1561820009-8bef03ebf8e5?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTM3fHxwb3J0cmFpdHxlbnwwfDJ8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60")
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@
*/
package com.mobiledevpro.navigation

import android.content.Intent
import android.net.Uri
import android.util.Log
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavType
Expand Down Expand Up @@ -193,7 +198,11 @@ fun NavGraphBuilder.chatListScreen() {
}
}

fun NavGraphBuilder.peopleListScreen(onNavigateTo: (Screen) -> Unit) {
@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.peopleListScreen(
transitionScope: SharedTransitionScope,
onNavigateTo: (Screen) -> Unit
) {
composable(
route = Screen.PeopleList.route
) {
Expand All @@ -202,8 +211,9 @@ fun NavGraphBuilder.peopleListScreen(onNavigateTo: (Screen) -> Unit) {
modules = { listOf(featurePeopleListModule) }
)

PeopleListScreen(
transitionScope.PeopleListScreen(
viewModel.uiState,
animatedVisibilityScope = this,
onNavigateToProfile = { profileId: Int ->
Screen.PeopleProfile.routeWith(profileId.toString())
.also(onNavigateTo)
Expand All @@ -212,7 +222,9 @@ fun NavGraphBuilder.peopleListScreen(onNavigateTo: (Screen) -> Unit) {
}
}

@OptIn(ExperimentalSharedTransitionApi::class)
fun NavGraphBuilder.peopleProfileScreen(
transitionScope: SharedTransitionScope,
onNavigateBack: () -> Unit,
onNavigateTo: (Screen) -> Unit
) {
Expand All @@ -228,10 +240,22 @@ fun NavGraphBuilder.peopleProfileScreen(

peopleProfile ?: return@composable

PeopleProfileScreen(
val context = LocalContext.current

val openWebLink: (Uri) -> Unit = { uri ->
Intent(Intent.ACTION_VIEW).apply {
data = uri
}.also { intent ->
context.startActivity(intent)
}
}

transitionScope.PeopleProfileScreen(
peopleProfile,
animatedVisibilityScope = this,
onBackPressed = onNavigateBack,
onOpenChatWith = {}
onOpenChatWith = {},
onOpenSocialLink = openWebLink
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
*/
package com.mobiledevpro.navigation.graph

import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
Expand All @@ -33,21 +35,30 @@ import com.mobiledevpro.navigation.peopleProfileScreen
*
*/

@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun PeopleNavGraph(
modifier: Modifier = Modifier,
navController: NavHostController
) {
NavHost(
navController = navController,
startDestination = Screen.PeopleList.route,
modifier = modifier,
) {
SharedTransitionLayout {
NavHost(
navController = navController,
startDestination = Screen.PeopleList.route,
modifier = modifier,
) {

peopleListScreen(onNavigateTo = navController::navigateTo)
peopleProfileScreen(
onNavigateTo = navController::navigateTo,
onNavigateBack = navController::navigateUp
)
peopleListScreen(
this@SharedTransitionLayout,
onNavigateTo = navController::navigateTo
)

peopleProfileScreen(
this@SharedTransitionLayout,
onNavigateTo = navController::navigateTo,
onNavigateBack = navController::navigateUp
)
}
}

}
5 changes: 5 additions & 0 deletions doc/RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@
- Demo

## v0.1.0-build-2

- Added transition Animation for People profile. New transition animation is available in
androidx.compose.ui since v1.7.0 alpha and navigation-compose 2.8.0 alpha.

## v0.1.0-build-3
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
package com.mobiledevpro.peoplelist.view

import android.widget.Toast
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
Expand All @@ -32,7 +35,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
Expand All @@ -41,14 +43,14 @@ import com.mobiledevpro.peoplelist.view.component.ProfileCard
import com.mobiledevpro.ui.component.ScreenBackground
import com.mobiledevpro.ui.ext.dp
import com.mobiledevpro.ui.ext.statusBarHeight
import com.mobiledevpro.ui.theme.AppTheme
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow


@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
fun PeopleListScreen(
fun SharedTransitionScope.PeopleListScreen(
stateFlow: StateFlow<PeopleProfileUIState>,
animatedVisibilityScope: AnimatedVisibilityScope,
onNavigateToProfile: (profileId: Int) -> Unit
) {

Expand All @@ -66,6 +68,7 @@ fun PeopleListScreen(
is PeopleProfileUIState.Success ->
PeopleList(
list = (uiState as PeopleProfileUIState.Success).profileList,
animatedVisibilityScope = animatedVisibilityScope,
onProfileClick = onNavigateToProfile
)

Expand Down Expand Up @@ -118,8 +121,13 @@ private fun Loading() {
}
}

@OptIn(ExperimentalSharedTransitionApi::class)
@Composable
private fun PeopleList(list: List<PeopleProfile>, onProfileClick: (profileId: Int) -> Unit) {
private fun SharedTransitionScope.PeopleList(
list: List<PeopleProfile>,
animatedVisibilityScope: AnimatedVisibilityScope,
onProfileClick: (profileId: Int) -> Unit
) {

//Cause content is drawn edge-to-edge, let's set the top-padding for the first element in the list
val statusBarHeight: Dp = WindowInsets.statusBarHeight().dp()
Expand All @@ -130,21 +138,27 @@ private fun PeopleList(list: List<PeopleProfile>, onProfileClick: (profileId: In
key = { _, item -> item.listKey() }
) { index, profile ->
ProfileCard(
modifier = Modifier.then(
if (index == 0)
Modifier.padding(top = statusBarHeight)
else
Modifier
),


modifier = Modifier
.then(
if (index == 0)
Modifier.padding(top = statusBarHeight)
else
Modifier
)
.sharedElement(
state = rememberSharedContentState(
key = "image-${profile.photo}"
),
animatedVisibilityScope = animatedVisibilityScope,
),
item = profile,
onClick = { onProfileClick(profile.id) })
}
}
}


/*
@OptIn(ExperimentalSharedTransitionApi::class)
@Preview
@Composable
fun PeopleListPreview() {
Expand All @@ -154,4 +168,6 @@ fun PeopleListPreview() {
onNavigateToProfile = { }
)
}
}
}
*/
Loading

0 comments on commit 7d1bebe

Please sign in to comment.