Last active
January 19, 2022 01:27
-
-
Save shakil807g/313f01dfaadb9e69d29beca261c0f5a2 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.facr.fotbal.common | |
import androidx.compose.animation.animateContentSize | |
import androidx.compose.foundation.Image | |
import androidx.compose.foundation.background | |
import androidx.compose.foundation.layout.* | |
import androidx.compose.foundation.rememberScrollState | |
import androidx.compose.foundation.verticalScroll | |
import androidx.compose.material.MaterialTheme | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.derivedStateOf | |
import androidx.compose.runtime.getValue | |
import androidx.compose.runtime.remember | |
import androidx.compose.ui.Alignment | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.graphics.vector.ImageVector | |
import androidx.compose.ui.layout.ContentScale | |
import androidx.compose.ui.layout.Layout | |
import androidx.compose.ui.platform.LocalDensity | |
import androidx.compose.ui.res.vectorResource | |
import androidx.compose.ui.unit.Constraints | |
import androidx.compose.ui.unit.dp | |
import androidx.compose.ui.unit.lerp | |
import androidx.compose.ui.util.lerp | |
import com.facr.fotbal.R | |
import dev.chrisbanes.accompanist.insets.statusBarsPadding | |
import kotlin.math.max | |
import kotlin.math.min | |
private val MinImageOffset = 2.dp | |
private val MaxImageOffset = 30.dp | |
private val ExpandedImageWidth = 200.dp | |
private val ExpandedImageHeight = 80.dp | |
private val CollapsedImageWidth = 160.dp | |
private val CollapsedImageHeight = 50.dp | |
private val MinTitleOffset = 56.dp | |
private val TransparentSpace = 120.dp | |
private val GradientScroll = TransparentSpace - MinTitleOffset | |
private val MaxTitleOffset = MinTitleOffset + GradientScroll + 100.dp | |
private val HzPadding = Modifier.padding(horizontal = 24.dp) | |
@Composable | |
fun AnimatedHeaderLayout( | |
hideableHeader: @Composable (getAlpha: () -> Float) -> Unit, | |
body: @Composable ColumnScope.(getAlpha: () -> Float) -> Unit | |
) { | |
Box(Modifier.fillMaxSize()) { | |
val scroll = rememberScrollState(0) | |
val collapseRange = with(LocalDensity.current) { (MaxTitleOffset - MinTitleOffset).toPx() } | |
val collapseFraction by remember(collapseRange) { | |
derivedStateOf { | |
(scroll.value / collapseRange).coerceIn( | |
0f, | |
1f | |
) | |
} | |
} | |
val getAlpha = { (1f - collapseFraction).coerceIn(0f, 1f) } | |
/* val alphaFraction by remember(collapseRange) { | |
derivedStateOf { | |
(1f - collapseFraction).coerceIn( | |
0f, | |
1f | |
) | |
} | |
}*/ | |
Spacer( | |
modifier = Modifier | |
.height(TransparentSpace) | |
.fillMaxWidth() | |
.background(MaterialTheme.colors.primary) | |
) | |
Column { | |
Spacer( | |
modifier = Modifier | |
.fillMaxWidth() | |
.height(MinTitleOffset) | |
) | |
Column( | |
modifier = Modifier.verticalScroll(scroll) | |
) { | |
Spacer(Modifier.height(GradientScroll)) | |
Box(modifier = Modifier.background(MaterialTheme.colors.primary)) { | |
Column( | |
modifier = Modifier | |
.fillMaxWidth() | |
.padding(top = 100.dp) | |
.background(MaterialTheme.colors.background) | |
.align(Alignment.TopCenter) | |
.animateContentSize() | |
) { | |
body(getAlpha) | |
} | |
hideableHeader(getAlpha) | |
} | |
} | |
} | |
CollapsingImageLayout( | |
collapseFraction = collapseFraction, | |
modifier = HzPadding.then(Modifier.statusBarsPadding()) | |
) { | |
Image( | |
ImageVector.vectorResource(id = R.drawable.app_logo), | |
contentScale = ContentScale.Fit, | |
modifier = Modifier.fillMaxSize(), | |
contentDescription = null | |
) | |
} | |
} | |
} | |
@Composable | |
private fun CollapsingImageLayout( | |
collapseFraction: Float, | |
modifier: Modifier = Modifier, | |
content: @Composable () -> Unit | |
) { | |
Layout( | |
modifier = modifier, | |
content = content | |
) { measurables, constraints -> | |
check(measurables.size == 1) | |
val imageMaxWidth = min(ExpandedImageWidth.roundToPx(), constraints.maxWidth) | |
val imageMinWidth = max(CollapsedImageWidth.roundToPx(), constraints.minWidth) | |
val imageWidth = lerp(imageMaxWidth, imageMinWidth, collapseFraction) | |
val imageMaxHeight = min(ExpandedImageHeight.roundToPx(), constraints.maxWidth) | |
val imageMinHeight = max(CollapsedImageHeight.roundToPx(), constraints.minWidth) | |
val imageHeight = lerp(imageMaxHeight, imageMinHeight, collapseFraction) | |
val imagePlaceable = measurables[0].measure(Constraints.fixed(imageWidth, imageHeight)) | |
val imageY = lerp(MaxImageOffset, MinImageOffset, collapseFraction).roundToPx() | |
/*val imageX = lerp( | |
(constraints.maxWidth - imageWidth) / 2, // centered when expanded | |
constraints.maxWidth - imageWidth, // right aligned when collapsed | |
collapseFraction | |
)*/ | |
val imageX = (constraints.maxWidth - imageWidth) / 2 | |
layout( | |
width = constraints.maxWidth, | |
height = imageY + imageHeight | |
) { | |
imagePlaceable.place(imageX, imageY) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.facr.fotbal.ui.dashboard.home | |
import androidx.compose.animation.ExperimentalAnimationApi | |
import androidx.compose.foundation.ExperimentalFoundationApi | |
import androidx.compose.foundation.layout.* | |
import androidx.compose.material.ExperimentalMaterialApi | |
import androidx.compose.material.MaterialTheme | |
import androidx.compose.material.Text | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.mutableStateOf | |
import androidx.compose.runtime.remember | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.res.stringResource | |
import androidx.compose.ui.text.style.TextAlign | |
import androidx.compose.ui.unit.dp | |
import androidx.lifecycle.viewmodel.compose.viewModel | |
import androidx.paging.LoadState | |
import androidx.paging.compose.collectAsLazyPagingItems | |
import com.facr.fotbal.R | |
import com.facr.fotbal.common.AnimatedHeaderLayout | |
import com.facr.fotbal.common.VerticleSpace | |
import com.facr.fotbal.domain.model.matches.Match | |
import com.facr.fotbal.ui.FacrViewModel | |
import com.facr.fotbal.ui.theme.facrTopAppBarHeading | |
import com.facr.fotbal.util.Constants | |
import com.facr.fotbal.util.Constants.Padding100dp | |
import com.facr.fotbal.util.Constants.Padding10dp | |
import com.facr.fotbal.util.Constants.Padding16dp | |
import com.facr.fotbal.util.Constants.Padding48dp | |
import com.facr.fotbal.util.Constants.Padding80dp | |
import com.facr.fotbal.util.Constants.Padding8dp | |
@ExperimentalFoundationApi | |
@ExperimentalAnimationApi | |
@ExperimentalMaterialApi | |
@Composable | |
fun HomeScreen( | |
facrViewModel: FacrViewModel, | |
onEventClicked: (Match) -> Unit | |
) { | |
val homeScreenViewModel: HomeScreenViewModel = viewModel() | |
val favClubMatches = homeScreenViewModel.favClubsUpCompingMatch.collectAsLazyPagingItems() | |
val favClubRecentMatches = homeScreenViewModel.favClubsRecentMatches.collectAsLazyPagingItems() | |
val selectedIndex = remember { mutableStateOf(0) } | |
AnimatedHeaderLayout(hideableHeader = { alphaFraction -> | |
UpcomingMatch( | |
favClubMatches, | |
onEventClicked, | |
getAlpha = alphaFraction | |
) { selectedIndex.value = it } | |
}) { alpha -> | |
Spacer( | |
modifier = Modifier.then(Modifier.requiredHeight(Padding100dp)) | |
) | |
if (favClubMatches.loadState.refresh is LoadState.NotLoading && favClubMatches.itemCount > 1) { | |
Indicator( | |
getAlpha = alpha, | |
selectedIndex = selectedIndex.value, | |
itemSize = favClubMatches.itemCount | |
) | |
} | |
Text( | |
text = stringResource(R.string.dashboard_currentResult), | |
textAlign = TextAlign.Left, | |
color = MaterialTheme.colors.primary, | |
style = facrTopAppBarHeading(), | |
modifier = Modifier | |
.fillMaxWidth() | |
.padding( | |
top = Constants.Padding19dp, | |
bottom = Constants.Padding25dp, | |
start = Constants.Padding27dp, | |
end = Padding10dp | |
) | |
) | |
RecentMatchesResult(modifier = Modifier.fillMaxWidth(), favClubRecentMatches) | |
Text( | |
text = stringResource(R.string.dashboard_myTeams), | |
textAlign = TextAlign.Left, | |
color = MaterialTheme.colors.primary, | |
style = facrTopAppBarHeading(), | |
modifier = Modifier | |
.fillMaxWidth() | |
.padding( | |
start = Constants.Padding27dp, | |
end = Padding16dp, | |
top = Padding48dp, | |
bottom = Padding8dp | |
) | |
) | |
SelectedClubs(facrViewModel = facrViewModel, clubs = homeScreenViewModel.savedClubs) | |
Text( | |
text = stringResource(R.string.dashboard_myCompetitions), | |
textAlign = TextAlign.Left, | |
color = MaterialTheme.colors.primary, | |
style = facrTopAppBarHeading(), | |
modifier = Modifier | |
.fillMaxWidth() | |
.padding( | |
start = Constants.Padding27dp, | |
end = Padding16dp, | |
top = Padding16dp, | |
bottom = Padding8dp | |
) | |
) | |
SelectedCompetitions( | |
facrViewModel = facrViewModel, | |
homeScreenViewModel.savedCompetitions, | |
) | |
VerticleSpace(Padding80dp) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.facr.fotbal.ui.dashboard.home | |
import androidx.compose.foundation.background | |
import androidx.compose.foundation.horizontalScroll | |
import androidx.compose.foundation.layout.* | |
import androidx.compose.foundation.rememberScrollState | |
import androidx.compose.foundation.shape.CircleShape | |
import androidx.compose.material.MaterialTheme | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.collectAsState | |
import androidx.compose.runtime.getValue | |
import androidx.compose.runtime.remember | |
import androidx.compose.ui.Alignment | |
import androidx.compose.ui.Modifier | |
import androidx.compose.ui.draw.clip | |
import androidx.compose.ui.graphics.graphicsLayer | |
import androidx.compose.ui.unit.Dp | |
import androidx.compose.ui.unit.dp | |
import androidx.lifecycle.viewmodel.compose.viewModel | |
import androidx.paging.LoadState | |
import androidx.paging.compose.LazyPagingItems | |
import com.facr.fotbal.common.Destination | |
import com.facr.fotbal.common.HorizontalSpace | |
import com.facr.fotbal.domain.model.matches.Match | |
import com.facr.fotbal.ui.LocalNavigator | |
import com.facr.fotbal.ui.item.FacrFinishedMatchItem | |
import com.facr.fotbal.ui.item.FacrUpComingMatchItem | |
import com.facr.fotbal.ui.item.PlaceHolderItem | |
import com.facr.fotbal.util.Constants.Padding16dp | |
import com.facr.fotbal.util.Constants.Padding4dp | |
import com.facr.fotbal.util.Pager | |
import com.facr.fotbal.util.PagerState | |
@Composable | |
fun UpcomingMatch( | |
matchesList: LazyPagingItems<Match>, | |
onEventClicked: (Match) -> Unit, | |
getAlpha: (() -> Float)? = null, | |
selectedIndex: (Int) -> Unit, | |
) { | |
val navigator = LocalNavigator.current!! | |
val homeScreenViewModel: HomeScreenViewModel = viewModel() | |
val matchEvents by homeScreenViewModel.savedMatchEvents.collectAsState(initial = emptySet()) | |
val pagerState = remember() { PagerState() } | |
if (matchesList.loadState.refresh is LoadState.NotLoading && matchesList.itemCount > 0) { | |
pagerState.maxPage = matchesList.itemCount - 1 | |
Box( | |
modifier = Modifier | |
.fillMaxWidth() | |
.height(200.dp) | |
) { | |
Pager( | |
state = pagerState, | |
modifier = Modifier | |
.fillMaxSize() | |
.graphicsLayer { | |
alpha = getAlpha?.invoke() ?: 1f | |
} | |
) { | |
selectedIndex(currentPage) | |
val item = matchesList[page] | |
item?.let { | |
if (it.isEventIcon) { | |
FacrUpComingMatchItem( | |
item, | |
onEventClicked = { | |
homeScreenViewModel.updateMatchEvent(matchEvents, it.id.toString()) | |
onEventClicked(it) | |
}, | |
isClickable = false, | |
matchEvent = matchEvents, | |
showEventIcon = item.isEventIcon | |
) | |
} else { | |
FacrFinishedMatchItem(match = it) { | |
navigator.navigate(Destination.MatchDetailScreen(it.id)) | |
} | |
} | |
} | |
} | |
} | |
} else { | |
PlaceHolderItem() | |
} | |
} | |
@Composable | |
fun Indicator( | |
getAlpha: (() -> Float)? = null, | |
selectedIndex: Int, | |
maxItems: Int = 5, | |
itemSize: Int, | |
indicatorSize: Dp = 8.dp, | |
horizontalSpace: Dp = Padding4dp | |
) { | |
val finalSize = kotlin.math.min(maxItems, itemSize) | |
val selectedModIndex = selectedIndex % finalSize | |
Row( | |
modifier = Modifier | |
.fillMaxWidth() | |
.requiredWidth(200.dp) | |
.horizontalScroll(rememberScrollState()) | |
.wrapContentSize(Alignment.Center) | |
.padding(top = Padding16dp, bottom = Padding16dp) | |
.graphicsLayer { | |
alpha = getAlpha?.invoke() ?: 1f | |
}, | |
horizontalArrangement = Arrangement.Center, | |
verticalAlignment = Alignment.CenterVertically | |
) { | |
(0 until finalSize).forEach { | |
val color = | |
if (selectedModIndex == it) MaterialTheme.colors.primary else MaterialTheme.colors.surface | |
Box( | |
modifier = Modifier | |
.requiredSize(indicatorSize) | |
.clip(CircleShape) | |
.background(color = color) | |
) | |
HorizontalSpace(horizontalSpace) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment