Stretch the surface along the entire height which is in row - android-jetpack-compose

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,
)
}
}
}
}

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))
}
}
})

Resizing text dynamically in LazyColumn depending on scroll - 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
}

BottomNavigationItems padding

Is there any way to remove this padding from the BottomNavigationItem?
Image
If I have very large text, I have to use ResponsiveText to manage this, but that's not what I intended. What I need is that it doesn't have that side padding/margin, both on the left and on the right, in order to occupy as much space as possible.
My code:
#Composable
fun BottomNavBar(
backStackEntryState: State<NavBackStackEntry?>,
navController: NavController,
bottomNavItems: List<NavigationItem>
) {
BottomNavigation(
backgroundColor = DarkGray.copy(alpha = 0.6f),
elevation = Dimen0,
modifier = Modifier
.padding(Dimen10, Dimen20, Dimen10, Dimen20)
.clip(RoundedCornerShape(Dimen13, Dimen13, Dimen13, Dimen13))
) {
bottomNavItems.forEach { item ->
val isSelected = item.route == backStackEntryState.value?.destination?.route
BottomNavigationItem(
icon = {
Icon(
painter = painterResource(id = item.icon.orZero()),
contentDescription = stringResource(id = item.title)
)
},
label = {
ResponsiveText(
text = stringResource(id = item.title),
textStyle = TextStyle14,
maxLines = 1
)
},
selectedContentColor = Color.White,
unselectedContentColor = Color.White,
alwaysShowLabel = true,
selected = isSelected,
onClick = {
navController.navigate(item.route) {
navController.graph.startDestinationRoute?.let { route ->
popUpTo(route = route) {
saveState = true
}
}
launchSingleTop = true
restoreState = true
}
},
modifier = if (isSelected) {
Modifier
.clip(RoundedCornerShape(Dimen13, Dimen13, Dimen13, Dimen13))
.background(color = DarkGray)
} else {
Modifier.background(color = Color.Unspecified)
}
)
}
}
}
Apparently this is currently (I am using compose version '1.2.0-rc03') not possible when using BottomNavigation, as there is padding set for each element in these lines:
.padding(horizontal = BottomNavigationItemHorizontalPadding)
Here is what is said about this value:
/**
* Padding at the start and end of a [BottomNavigationItem]
*/
private val BottomNavigationItemHorizontalPadding = 12.dp
[Solution]
Just copy BottomNavigation from androidx and remov this line:
.padding(horizontal = BottomNavigationItemHorizontalPadding)
However, it is necessary that the first and last elements still have padding, so add the innerHorizontalPaddings parameter to the your CustomBottomNavigation constructor
There are a few more changes, you can see the full code of my CustomBottomNavigation here
Example of usage:
CustomBottomNavigation(
...,
innerHorizontalPaddings = 12.dp
) {
items.forEach { item ->
BottomNavigationItem(
icon = {
Icon(...)
},
label = {
Text(
...
softWrap = false,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = 2.dp)
)
},
...
)
}
}
Another solution is to wrap the label in a BoxWithConstraints and draw outside of it:
BottomNavigationItem(
label = {
/**
* Because of [BottomNavigationItemHorizontalPadding] (12.dp), we need to
* think (and draw) outside the box.
*/
BoxWithConstraints {
Text(
modifier = Modifier
.wrapContentWidth(unbounded = true)
.requiredWidth(maxWidth + 24.dp), // 24.dp = the padding * 2
text = "Centered text and clipped at the end if too long",
softWrap = false,
textAlign = TextAlign.Center
)
}
},
...
)
To get a little bit of padding, you can set requiredWidth(maxWidth + 18.dp).
With this solution, you don't need to copy the enire BottomNavigation :)

goneMargin in ConstraintLayout with Compose

I'm trying to use ConstraintLayout in Compose. I want the second Composable to be constrained at the end of the first Composable. The first composable can exist or not. In each case, I would like to have different margins. Thus, I use goneMargin but this seems to not be respected. Do I need to do something else?
ConstraintLayout {
val (firstItemRef, secondItemRef) = createRefs()
if (isFirstItemVisible) {
Text(
text = "First",
modifier = Modifier.constrainAs(firstItemRef) {
top.linkTo(anchor = parent.top)
start.linkTo(anchor = parent.start)
}
)
}
Text(
text = "Second",
modifier = Modifier.constrainAs(secondItemRef) {
top.linkTo(anchor = parent.top)
start.linkTo(anchor = firstItemRef.end, margin = 8.dp, goneMargin = 16.dp)
}
)
}
As a workaround, we could do something like that, but this seems to counterfeit the purpose of goneMargin
Text(
text = "Second",
modifier = Modifier.constrainAs(secondItemRef) {
val margin = if (isFirstItemVisible) 8.dp else 16.dp
val end = if (isFirstItemVisible) firstItemRef.end else parent.end
top.linkTo(anchor = parent.top)
start.linkTo(anchor = end, margin = margin)
}
)
You have to use the visibility property in ConstrainScope like this:
ConstraintLayout(Modifier.fillMaxSize()) {
val (firstItemRef, secondItemRef) = createRefs()
Text(
text = "First",
modifier = Modifier.constrainAs(firstItemRef) {
top.linkTo(anchor = parent.top)
start.linkTo(anchor = parent.start)
// >> This is what you want <<<
visibility = if (isFirstItemVisible) Visibility.Visible else Visibility.Gone
}
)
Text(
text = "Second",
modifier = Modifier.constrainAs(secondItemRef) {
top.linkTo(anchor = parent.top)
start.linkTo(anchor = firstItemRef.end, margin = 8.dp, goneMargin = 16.dp)
}
)
}

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