Resizing text dynamically in LazyColumn depending on scroll - Jetpack Compose - android-jetpack-compose

I want to make the text to be resizing dynamically when I scroll and them to stay that size when I stop scrolling.
For example here the number 4 to be 20.sp and the numbers 3 and 5 to be 18.sp (one index away) and 2 - 6 to be 16.sp and so on. I think that could be created with
Modifier.scale(), but have not found a way for now.
#OptIn(ExperimentalSnapperApi::class)
#Composable
fun Example() {
val listStateHour = rememberLazyListState()
val hourTexts: List<String> = (1..23).map { it.toString().padStart(2, '0') }
Box(
modifier = Modifier.background(color = Color.LightGray).fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.height(32.dp)
.fillMaxWidth()
.background(color = Color.Black.copy(0.2f)),
) {}
LazyColumn(
modifier = Modifier.height(200.dp),
state = listStateHour,
horizontalAlignment = Alignment.Start,
contentPadding = PaddingValues(vertical = 116.dp),
flingBehavior = rememberSnapperFlingBehavior(lazyListState = listStateHour)
) {
itemsIndexed(hourTexts) { index, item ->
Box(
modifier = Modifier.height(32.dp),
contentAlignment = Alignment.Center
) {
Text(
text = item,
color = Color.Black,
fontSize = 20.sp
)
}
}
}
}
}
External dependencies that I am using -> implementation 'dev.chrisbanes.snapper:snapper:0.3.0'

You can observe listStateHour.firstVisibleItemIndex and calculate a scaling factor depending on the distance an item has to that value.
I didn't test it with the flingBehaviour you're using but this might help you:
val listStateHour = rememberLazyListState()
val firstVisibleItemIndex by remember { derivedStateOf { listStateHour.firstVisibleItemIndex } }
val hourTexts: List<String> = (1..23).map { it.toString().padStart(2, '0')
Box(
modifier = Modifier
.background(color = Color.LightGray)
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.height(32.dp)
.fillMaxWidth()
.background(color = Color.Black.copy(0.2f)),
) {}
LazyColumn(
modifier = Modifier.height(200.dp),
state = listStateHour,
horizontalAlignment = Alignment.Start,
contentPadding = PaddingValues(vertical = 116.dp)
) {
itemsIndexed(hourTexts) { index, item ->
Box(
modifier = Modifier.height(32.dp),
contentAlignment = Alignment.Center
) {
Text(
text = item,
color = Color.Black,
fontSize = 20.sp,
modifier = Modifier.scale(
scaleText(
firstVisibleIndex = firstVisibleItemIndex,
itemIndex = index
)
)
)
}
}
}
}
fun scaleText(firstVisibleIndex: Int, itemIndex: Int): Float {
val distance = abs( firstVisibleIndex - itemIndex)
return 1f - distance * .2f
}

Related

the view outside bottom sheet is called when sheet expanded

I'm using BottomSheetScaffold for BottomSheet.
I followed some sample and it worked.
but when I click outside the BottomSheet, the view's click event is called behind the sheet.
I want to prevent the view click when the Sheet is expanded.
Is there any way?
here is my code
val bottomSheetState = rememberBottomSheetState(
initialValue = BottomSheetValue.Collapsed,
)
val scaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = bottomSheetState,
)
val coroutineScope = rememberCoroutineScope()
val purchaseButtonHeight = 76
val closedSheetHeaderHeight = 50
val sheetHeaderRadius = 24
BottomSheetScaffold(
modifier = Modifier.pointerInput(Unit) {
detectTapGestures(
onTap = {
println("TAG : detectTapGestures ${it}")
coroutineScope.launch {
if (bottomSheetState.isCollapsed) {
bottomSheetState.expand()
} else {
bottomSheetState.collapse()
}
}
})
},
scaffoldState = scaffoldState,
sheetShape = RoundedCornerShape(
topStart = sheetHeaderRadius.dp,
topEnd = sheetHeaderRadius.dp
),
sheetPeekHeight = (purchaseButtonHeight + closedSheetHeaderHeight).dp,
sheetContent = {
Box(
modifier = Modifier
.wrapContentHeight()
.padding(horizontal = 16.dp),
contentAlignment = Alignment.BottomCenter
) {
Column {
for (i in 0 until 5) {
Text(
text = "Item ${i}",
modifier = Modifier
.height(50.dp)
.fillMaxWidth(),
textAlign = TextAlign.Center
)
}
Spacer(modifier = Modifier.height(purchaseButtonHeight.dp))
}
}
})

Stretch the surface along the entire height which is in row

I have a lazy column in which there is a list of cards in which there is a row and two columns in it, the height of the first column is much larger than the second. The second column has text with a different background made using a surface. The fillMaxHeight modifiers do not work. Already tried to do it by other means:
1)
Row(
modifier = Modifier
.height(IntrinsicSize.Min)
However, there is a problem, this only works with static components, and this is a lazy line in my columns, and the composition starts to crash
2) Get the height in this way.
//Save height
val heightIs = remember {
mutableStateOf(0.dp)
}
//Get the context
val localDensity = LocalDensity.current
Column(modifier = Modifier.fillMaxWidth(0.7f).onSizeChanged{cord->
heightIs.value = with(localDensity){
cord.height.toDp()
}
The problem with this solution is that when there is no lazy row, it doesn't properly distribute the height.
Short code:
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 2.dp, vertical = 4.dp), elevation = 4.dp
) {
Row(modifier = Modifier.height(IntrinsicSize.Min)) {
Column(Modifier.fillMaxWidth(0.7f)) {
Surface(
color = MaterialTheme.colors.primaryVariant,
modifier = Modifier.fillMaxWidth(),
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = historyOrderItem.id.toString(),
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(4.dp)
)
Text(
text = historyOrderItem.status,
style = MaterialTheme.typography.h6,
modifier = Modifier.padding(4.dp)
)
}
}
LazyRow(modifier = modifier.fillMaxWidth()) {
items(items = historyOrderItem.items, key = { it.itemId }) {
ItemDoneOrderShort(it)
}
}
Spacer(modifier = Modifier.weight(1f))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End
) {
TextButton(onClick = { onCopy(historyOrderItem) }) {
Text(
text = "ПОВТОРИТЬ",
style = MaterialTheme.typography.h6,
textAlign = TextAlign.Right,
maxLines = 1,
)
}
}
}
Surface(color = MaterialTheme.colors.colorAccent) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = "Оплатить",
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h5,
)
}
}
}
}

expand Image as much as screen size jetpack compose

I have a LazyColumn and some childs in it like below
LazyColumn(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
item {
Child(
modifier = Modifier,
firstImage = fakeImage,
secondImage = fakeImage,
onImageClick = {}
)
}
item {
Child(
modifier = Modifier,
firstImage = fakeImage,
secondImage = fakeImage,
onImageClick = {}
)
}
}
here is what is inside of TwoPiecesLayout
#ExperimentalMaterialApi
#Composable
fun Child(
modifier: Modifier,
firstImage: Image,
secondImage: Image,
onImageClick: (Image) -> Unit
) {
val height = (LocalConfiguration.current.screenWidthDp / 2) - 56
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
ImageCell(
modifier = Modifier
.weight(
weight = 1F
)
.height(
height = height.dp
),
image = firstImage,
onImageClick = {
onImageClick(firstImage)
}
)
ImageCell(
modifier = Modifier
.weight(
weight = 3F
)
.height(
height = height.dp
),
image = secondImage,
onImageClick = {
onImageClick(secondImage)
}
)
}
}
when every of Images in Child clicked I want to expand their size as much as screen's size just like the material design choreography
https://storage.cloud.google.com/non-spec-apps/mio-direct-embeds/videos/FADE.mp4
how can I do this?
This is not just for image, with basically any Composable, you can apply this method
var expanded by remember { mutableStateOf (false) }
val animF by animateFloatAsState(
initialState = 0.25f,
targetState = if (expanded) 1f else 0.25f
)
MyComposable(
Modifier.fillMaxSize(animF) // pass the animated Fraction here
.clickable { expanded = !expanded }
)
This will oscillate between 0.25 of the entire screen to 1f of the same, upon clicking the Composable.
See? Super-easy, barely an inconvenience.

HorizontalPager detect swipe offset

I've a HorizontalPager with 3 items.
I need to know exact offset according to user's swipe
HorizontalPager(
count = 3
) {
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = ""
)
}
In Compose many views have some state parameter, using which you can check its state.
In case of pager, it the state has currentPage and currentPageOffset info. To calculate final offset you also need to check view size with Modifier.onSizeChanged or BoxWithConstraints.
I'm using derivedStateOf to prevent redundant recompositions.
val pagerState = rememberPagerState()
val (pagerWidth, setPagerWidth) = remember { mutableStateOf<Int?>(null) }
val scrollOffset by remember(pagerWidth) {
derivedStateOf {
if (pagerWidth == null) return#derivedStateOf 0f
(pagerState.currentPage + pagerState.currentPageOffset) * pagerWidth
}
}
HorizontalPager(
state = pagerState,
count = 3,
modifier = Modifier
.onSizeChanged {
setPagerWidth(it.width)
}
) {
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = ""
)
}
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = "",
modifier = Modifier
.fillMaxWidth()
.offset(x = with(LocalDensity.current) { -scrollOffset.toDp() })
)

How to Snap the Row/Column elements to end in JetPack Compose

I am using the below code to show the textviews, image and a textfield to show at each corners after the image (start and end). It is basically a Card with Image on the start and later a column with a two text views and with another column with a textview and basictextfield for input.
#Composable
fun BaseCard() {
Card(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(Alignment.CenterVertically, true)
.wrapContentHeight()
.clip(RoundedCornerShape(8.dp)),
elevation = 10.dp,
backgroundColor = Color.White
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
)
{
Image(
modifier = Modifier
.size(35.dp, 35.dp) //50dp
.clip(RoundedCornerShape(8.dp))
.clickable {
navController.navigate("meta") {
launchSingleTop = true
}
},
painter = img,
alignment = Alignment.Center,
contentDescription = "",
contentScale = ContentScale.Crop,
)
Spacer(modifier = Modifier.width(8.dp))
Column(horizontalAlignment = Alignment.Start) {
Text(
text = "US Dollar" + "($)",
color = Color.Gray,
style = Typography.subtitle2
)
Text(
text = "USD",
Modifier.padding(0.dp),
color = MaterialTheme.colors.onPrimary,
style = Typography.h5,
)
}
Spacer(modifier = Modifier.width(8.dp))
Column(horizontalAlignment = Alignment.End) {
Text(
text = "Amount",
color = Color.Gray,
style = Typography.subtitle2
)
BasicTextField(
value = typedValue,
onValueChange = {
----
},
textStyle = LocalTextStyle.current.copy(
fontFamily = NunitoFontFamily,
fontWeight = FontWeight.W600,
letterSpacing = 0.sp,
fontSize = 24.sp,
textAlign = TextAlign.End
),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
)
)
}
}
}
}
However, it is showing up as below.
How to remove the extra space and align the elements properly at each end?
There are multiple ways to implement what you ask
One is using Spacer(modifier=Modifier.weight(1f) instead of
Spacer(modifier = Modifier.width(8.dp))
for the Spacer here, since it's inside a Row it will take all the space that don't have weight modifier.
// Change this Spacer's modifier to Modifier.weight(1f)
Spacer(modifier = Modifier.width(8.dp))
Column(horizontalAlignment = Alignment.End) {
Text(
text = "Amount",
color = Color.Gray,
style = Typography.subtitle2
)
BasicTextField(
value = typedValue,
onValueChange = {
----
},
textStyle = LocalTextStyle.current.copy(
fontFamily = NunitoFontFamily,
fontWeight = FontWeight.W600,
letterSpacing = 0.sp,
fontSize = 24.sp,
textAlign = TextAlign.End
),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
)
)
}
Second one is to use a Box instead of Row and have 2 composables one aligned to
Box(modifier = Modifier
.fillMaxWidth()
.padding(8.dp)) {
Column(modifier = Modifier.align(Alignment.CenterStart)) {
}
Column(modifier = Modifier.align(Alignment.CenterEnd)) {
}
}

Resources