Jetpack Compose Constraint Layout Constraints not linking as expected - android

I'm using constrainAs with Jetpack Compose to constrain a list of options.
To the left of the text there is a space, I believe this is caused by the fact that I constrain the text box to the start to the parent and the end to the start of the switch, but I need the text to wrap as shown in the second option so I think I need both of those constraints. I have tried several constraints but cannot figure out how to have the text left justified and have the wrapping. The problem is depicted in red in the image.
Also, I cannot figure out how to have the same spacing between the title and the description. This is shown in blue in the picture. I have the description constrained to the bottom of the title but when it wraps the text box becomes larger and is moved up and because the text gets centered it creates different spacing.
I have attached an image and the code.
#Composable
fun SwitchRow(title: String, description: String, enabled: Boolean) {
Box(modifier = Modifier
.height(66.dp)
.fillMaxWidth()
.padding(top = 12.dp, start = 16.dp, end = 8.dp, bottom = 12.dp)
) {
ConstraintLayout(
modifier = Modifier
.fillMaxWidth()
) {
val (titleText, descriptionText, switch) = createRefs()
Text(
text = title,
modifier = Modifier
.padding(bottom = 16.dp)
.constrainAs(titleText) {
start.linkTo(parent.start)
top.linkTo(parent.top)
},
color = MyAppTheme.colors.ice,
fontSize = 18.sp,
fontFamily = FontFamily(Font(R.font.barlow_regular, FontWeight.Normal)),
textAlign = TextAlign.Start
)
Text(
text = description,
modifier = Modifier
.wrapContentSize()
.constrainAs(descriptionText) {
start.linkTo(parent.start)
end.linkTo(switch.start)
top.linkTo(titleText.bottom)
bottom.linkTo(parent.bottom)
width = Dimension.fillToConstraints
},
color = MyAppTheme.colors.chalk,
fontSize = 14.sp,
fontFamily = FontFamily(Font(R.font.barlow_regular, FontWeight.Normal)),
maxLines = 2,
textAlign = TextAlign.Start
)
val checkedState = remember { mutableStateOf(true) }
Switch(modifier = Modifier
.background(color = Color.Gray)
.constrainAs(switch) {
top.linkTo(parent.top)
end.linkTo(parent.end)
},
enabled = enabled,
checked = checkedState.value,
onCheckedChange = { checkedState.value = it },
colors = SwitchDefaults.colors(
checkedThumbColor = MyAppTheme.colors.envy,
checkedTrackColor = MyAppTheme.colors.darkerEnvy,
uncheckedThumbColor = MyAppTheme.colors.navy,
uncheckedTrackColor = MyAppTheme.colors.darkerNavy,
),
)
}
}
}

I copy and paste the code into my editor and it gave me a lot of errors I couldn't solve. so I couldn't make you a copy and past the answer ... sorry for that.
But! look carefully!
the only thing you need to think about are Row() and Columns()
Insert this mindset into your way of thinking and it will make your life easy.
like CSS if you are familiar with web development (because I saw you wrote justify content).
look at this picture down bellow.
as you can see at the picture above there are two major properties of the rows, to Justify the content as you said you want to do. and you can list all the properties with ctrl + space
the first is horizontalArrangement = , and the second is verticalAlignment = as you can see in the code bellow.
Row(
horizontalArrangement = Arrangement.Start, // the properties you are looking for in a Row()
verticalAlignment = Alignment.CenterVertically, // // the properties you are looking for in a Row()
)
this is very confusing because a Column has really similar properties to justify its content too!
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
)
which the first is verticalArrangement = and the second is horizontalAlignment = and they are different words so be in focus when dealing with this deign so you wont confuse your self!
I included the property of the border in every row and column so you can see my way of thinking.
pro tip! use a lazy column so that the window size can be dynamic to your use.
after you will play with this it will be easier for you to make.
the weight() property of the modifier solves this problem.
i gave the left column 85% and the Switch button 15% percent you will see in the code where to modify it if you need to do so.
I created for you the simplest example I can make to fit your need that you can modify for you if you are encountering any more problems feel free to comment and ask.
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.lilmokeq.ui.theme.LilMokeQTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
LilMokeQTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background
) {
NotificationCenter(
)
}
}
}
}
}
#Composable
fun NotificationCenter() {
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp, start = 16.dp, end = 8.dp, bottom = 12.dp)
.background(color = Color.White)
) {
item {
// create a row with a X icon on the left and a title
// right next to it with a little padding
Row(
horizontalArrangement = Arrangement.Start, // the properties you are looking for in a Row()
verticalAlignment = Alignment.CenterVertically, // // the properties you are looking for in a Row()
modifier = Modifier
.fillMaxWidth()
.background(color = Color.White)
.border(1.dp, Color.Black)
) {
IconButton(
onClick = { /*TODO*/ }, modifier = Modifier.clip(CircleShape)
) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = "Close",
tint = Color.Black
)
}
Text(
text = "Notifications Preferences",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
// fontFamily = FontFamily(Font(R.font.roboto)), // for some reason i have a problem with this line
modifier = Modifier.padding(start = 8.dp)
)
}
Spacer(modifier = Modifier.height(8.dp))
Card(
elevation = 8.dp,
modifier = Modifier
.fillMaxWidth()
.padding(start = 8.dp, end = 8.dp)
.background(color = Color.White)
) {
// create a row that will contain two columns , the left column will contain the text "Your Account" and under it the text "important notifications about your account" and the right column will contain a switch button.
Row(
horizontalArrangement = Arrangement.SpaceAround,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.border(1.dp, Color.Black)
) {
Column(
// the left column
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.Start, // useful to justify content
modifier = Modifier
.background(color = Color.White)
.border(1.dp, Color.Black)
// set a width to the column
.weight(0.85f)
) {
Text(
text = "Your Account",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(start = 8.dp)
)
Text(
text = "important notifications about your account" + " and your account settings" + " and more information about" + "interesting stuff" + "lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis ",
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
modifier = Modifier.padding(start = 8.dp, end = 8.dp)
)
}
Column(
// the right column
horizontalAlignment = Alignment.End,
modifier = Modifier
.background(color = Color.White)
.border(1.dp, Color.Black)
// set a width to the column
.weight(0.15f)
) {
Switch(
checked = true,
onCheckedChange = { /*TODO*/ },
modifier = Modifier
.padding(end = 8.dp)
.border(1.dp, Color.Black)
)
}
} // end of row
} // end of card
Spacer(modifier = Modifier.height(8.dp))
Card(
elevation = 8.dp,
modifier = Modifier
.fillMaxWidth()
.padding(start = 8.dp, end = 8.dp)
.background(color = Color.White)
) {
// create a row that will contain two columns , the left column will contain the text "Your Account" and under it the text "important notifications about your account" and the right column will contain a switch button.
Row(
horizontalArrangement = Arrangement.SpaceAround,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.border(1.dp, Color.Black)
) {
Column(
// the left column
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.Start, // useful to justify content
modifier = Modifier
.background(color = Color.White)
.border(1.dp, Color.Black)
// set a width to the column
.weight(0.85f)
) {
Text(
text = "Second notification",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(start = 8.dp)
)
Text(
text = "important notifications about your account" + " and your account settings" + " and more information about" + "interesting stuff" + "lorem ipsum dolor sit amet, consectetur adipiscing elit,",
fontSize = 14.sp,
fontWeight = FontWeight.Normal,
modifier = Modifier.padding(start = 8.dp, end = 8.dp)
)
}
Column(
// the right column
horizontalAlignment = Alignment.End,
modifier = Modifier
.background(color = Color.White)
.border(1.dp, Color.Black)
// set a width to the column
.weight(0.15f)
) {
Switch(
checked = true,
onCheckedChange = { /*TODO*/ },
modifier = Modifier
.padding(end = 8.dp)
.border(1.dp, Color.Black)
)
}
} // end of row
} // end of card
}
}
}
the final result ^
this playlist covers a lot of information. watch it to get more ideas about your design and how to implement them.
https://www.youtube.com/watch?v=cDabx3SjuOY&list=PLQkwcJG4YTCSpJ2NLhDTHhi6XBNfk9WiC&ab_channel=PhilippLackner

I think below changes in your code will solve your problem, I am not use font style and colour same as yours.
To left/start alignment you have to remove .wrapContentSize() method.
Box(modifier = Modifier
.height(66.dp)
.fillMaxWidth()
.padding(top = 12.dp, start = 16.dp, end = 8.dp, bottom = 12.dp)
) {
ConstraintLayout(
modifier = Modifier
.fillMaxWidth()
) {
val (titleText, descriptionText, switch) = createRefs()
Text(
text = title,
modifier = Modifier
.constrainAs(titleText) {
start.linkTo(parent.start)
top.linkTo(switch.top)
},
color = Color.Black,
fontSize = 18.sp,
textAlign = TextAlign.Start
)
Text(
text = description,
modifier = Modifier
.constrainAs(descriptionText) {
start.linkTo(parent.start)
end.linkTo(switch.start)
top.linkTo(titleText.bottom)
width = Dimension.fillToConstraints
},
color = Color.Red,
fontSize = 14.sp,
textAlign = TextAlign.Start
)
val checkedState = remember { mutableStateOf(true) }
Switch(modifier = Modifier
.background(color = Color.Gray)
.constrainAs(switch) {
top.linkTo(parent.top)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
},
enabled = enabled,
checked = checkedState.value,
onCheckedChange = { checkedState.value = it },
colors = SwitchDefaults.colors(
checkedThumbColor = Color.Green,
checkedTrackColor = Color.Gray,
uncheckedThumbColor = Color.Cyan,
uncheckedTrackColor = Color.DarkGray,
),
)
}
}
But I think you have to change you function constraint structure as below code
#Composable
fun SwitchRow(title: String, description: String, enabled: Boolean) {
Box(
contentAlignment = Alignment.TopStart,
modifier = Modifier
.height(85.dp)
.padding(8.dp)
) {
ConstraintLayout(
modifier = Modifier
.fillMaxWidth()
) {
val (descriptionText, switch) = createRefs()
Column(
horizontalAlignment = Alignment.Start,
modifier = Modifier
.fillMaxWidth()
.padding(start = 25.dp)
.constrainAs(descriptionText) {
top.linkTo(parent.top)
start.linkTo(parent.start)
end.linkTo(switch.start)
},
) {
Text(
text = title,
color = Color.Black,
fontSize = 18.sp,
textAlign = TextAlign.Start
)
Spacer(modifier = Modifier.height(2.dp))
Text(
text = description,
color = Color.Red,
fontSize = 14.sp,
textAlign = TextAlign.Start,
maxLines = 2
)
}
val checkedState = remember { mutableStateOf(true) }
Switch(
modifier = Modifier
.background(color = Color.Gray)
.constrainAs(switch) {
top.linkTo(parent.top)
end.linkTo(parent.end)
},
enabled = enabled,
checked = checkedState.value,
onCheckedChange = { checkedState.value = it },
colors = SwitchDefaults.colors(
checkedThumbColor = Color.Green,
checkedTrackColor = Color.Gray,
uncheckedThumbColor = Color.Cyan,
uncheckedTrackColor = Color.DarkGray,
),
)
}
}
}
In both code you have to change padding and spacing as per your requirement.

Related

How to make Compose Text wrap container size

I have some Column with Icon and Text inside. Column is wrap the size of Text but i want to Column wrap Icon and long text move to another line
How It's looks now
How I want it to look like
#Composable
fun ServiceItem(
service: Service,
onItemClick: (service: Service) -> Unit
) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Card(
modifier = Modifier.padding(horizontal = 16.dp),
shape = RoundedCornerShape(itemBackgroundCornerSize),
backgroundColor = colorResource(id = R.color.gray)
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(itemSize)
.clickable(onClick = {
onItemClick(service)
})
) {
Image(
painter = rememberAsyncImagePainter(service.imageUrl),
contentDescription = null,
modifier = Modifier.padding(iconPadding)
)
}
}
Text(
text = service.title,
textAlign = TextAlign.Center,
style = itemTitleTextDefaultStyle(),
modifier = Modifier.padding(top = textTopPadding)
)
}
}

JetPack Compose Button with drawable

How can we achieve this in jetpack compose
I'm doing something like this
Button(
elevation = ButtonDefaults.elevation(
defaultElevation = 0.dp,
pressedElevation = 8.dp,
disabledElevation = 0.dp
),
onClick = { onClick },
shape = RoundedCornerShape(28.dp),
modifier = modifier
.fillMaxWidth()
.shadow(0.dp),
contentPadding = PaddingValues(15.dp),
colors = ButtonDefaults.buttonColors(backgroundColor = Color.White),
border = BorderStroke(1.dp, Color.Grey)
) {
Box(modifier = modifier.fillMaxWidth(),
contentAlignment = Alignment.Center) {
Icon(
imageVector = imageVector,
modifier = Modifier
.size(18.dp),
contentDescription = "drawable icons",
tint = Color.Unspecified
)
Spacer(modifier = Modifier.width(10.dp))
Text(
text = buttonText,
color = Color.Black,
textAlign = TextAlign.Center
)
}
}
So as you can see the Google logo is just left of the text I need it at the start of the box so how can I do this.
You can use align(Alignment.CenterStart) on the Icon's Modifier parameter to center the icon around the start of the Box Composable. This alignment will have priority over the Box's alignment parameter.
You can also delete the Spacer composable because the Box layout children are stacked one on top of the other in the composition order. So the Spacer composable is basically laying below the Text composable in the center.
If you want some space between the Icon and the Text, you could use some padding around the Icon instead.
Try this (It worked for me) :
Box(modifier = modifier.fillMaxWidth(),
contentAlignment = Alignment.Center) {
Icon(
imageVector = imageVector,
modifier = Modifier
.size(18.dp)
.align(Alignment.CenterStart),
contentDescription = "drawable icons",
tint = Color.Unspecified
)
Text(
text = buttonText,
color = Color.Black,
textAlign = TextAlign.Center
)
}
#Composable
fun GoogleButton(
modifier: Modifier = Modifier,
imageVector: ImageVector,
buttonText: String,
onClick: (isEnabled: Boolean) -> Unit = {},
enable: Boolean = true,
backgroundColor: Color,
fontColor: Color,
) {
Button(
onClick = { onClick(enable) },
modifier = modifier
.fillMaxWidth()
.shadow(0.dp)
.noInteractionClickable(enabled = false) { onClick(enable) },
elevation = ButtonDefaults.elevation(
defaultElevation = 0.dp,
pressedElevation = 0.dp,
hoveredElevation = 0.dp,
focusedElevation = 0.dp
),
shape = RoundedCornerShape(28.dp),
contentPadding = PaddingValues(15.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = backgroundColor,
contentColor = fontColor
),
border = BorderStroke(1.dp, MaterialTheme.colors.getButtonBorderStroke)
) {
Box(
modifier = Modifier
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Row(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.CenterStart)
) {
Spacer(modifier = Modifier.width(4.dp))
Icon(
imageVector = imageVector,
modifier = Modifier
.size(18.dp),
contentDescription = "drawable_icons",
tint = Color.Unspecified
)
}
Text(
modifier = Modifier.align(Alignment.Center),
text = buttonText,
color = MaterialTheme.colors.loginButtonTextColor,
textAlign = TextAlign.Center,
fontSize = 16.sp,
fontFamily = FontFamily(
Font(
R.font.roboto_medium
)
)
)
}
}
}
As suggested in other answers you can wrap the content with a Box.
As alternative you can simply use the RowScope of the Button without any container.
Just apply a weight(1f) modifier to the Text and an offset(x=- iconWidth/2).
Something like:
Button(
//....
) {
Icon(
imageVector = imageVector,
modifier = Modifier.size(iconWidth),
contentDescription = "drawable icons",
tint = Color.Unspecified
)
Text(
text = "Button",
color = Color.Black,
textAlign = TextAlign.Center,
modifier = Modifier
.weight(1f)
.offset(x= -iconWidth/2) //default icon width = 24.dp
)
}
If you want to use a Box, remove the contentAlignment = Alignment.Center in the Box and use:
Box(modifier = Modifier.fillMaxWidth()) {
Icon( /* ..... */ )
Text(
modifier = Modifier.fillMaxWidth(),
text = "buttonText",
textAlign = TextAlign.Center
)
}
Box doesn't provide bounds, so for longer texts, it causes overlapping. Row works better for me. Also, you can use Spacer here which is not possible for Box. In my case, I have used spacedBy as a replacement for Spacer:
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
painter,
contentDescription = null
)
Box(
modifier = Modifier.weight(1F),
contentAlignment = Alignment.Center
) {
Text(buttonText)
}
}
Box(contentAlignment = Center){
Icon(Modifier.align(CenterStart))
Text()
}

Android Compose resize image on available space

I'm new to Android Jetpack Compose and have the following layout problem:
It's a simple example to clarify my issue. I want that Button header to stay on top and this two text areas at the bottom.
The image should take the rest of the available space, depending on the screen size. I don't want a scrollbar or white areas at the top/bottom.
At the moment the image has a fixed size. How do I have to adjust the layout so that the image automatically adapts to the available space?
Here's the code:
#Composable
fun Greeting() {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
Spacer(
modifier = Modifier
.height(32.dp)
.fillMaxSize()
)
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 16.dp)
) {
Button(onClick = { /*TODO*/ }) {
Text(text = "Back")
}
Button(onClick = { /*TODO*/ }) {
Text(text = "Next")
}
}
Spacer(modifier = Modifier.height(24.dp))
Image(painter = painterResource(id = R.drawable.waterfall), contentDescription = "Instant post demo image", Modifier.height(200.dp))
Spacer(modifier = Modifier.height(24.dp))
Text(text = text, fontSize = 14.sp, modifier = Modifier.padding(start = 16.dp, end = 16.dp))
Spacer(modifier = Modifier.height(16.dp))
Text(text = text, fontSize = 14.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(start = 16.dp, end = 16.dp)
)
}
}
Thx in advance

Align Image with Text baseline in a Row

How can I align an Image with a Text's baseline in a Row. Modifier.alignByBaseline() works for the Texts but the Image doesn't participate in the alignment.
#Composable
fun Sample() {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
Text(
text = "One",
modifier = Modifier.alignByBaseline(),
fontSize = 40.sp
)
Image(
modifier = Modifier
.padding(horizontal = 8.dp)
.size(24.dp)
.alignBy(FirstBaseline),
painter = painterResource(id = R.drawable.ic_launcher_background),
contentDescription = "",
)
Text(
text = "two",
modifier = Modifier.alignByBaseline(),
fontSize = 40.sp
)
}
}
alignByBaseline aligns item by it's own baseline, not neighbours ones.
You can use paddingFromBaseline for Text and same value for image padding with verticalAlignment = Alignment.Bottom.
And to get actual value of baseline offset to pass it to the padding modifier, you need to wait for TextLayoutResult: it gets called when text layout is calculated depending on text size, font, etc.
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.Bottom,
modifier = Modifier.fillMaxWidth()
) {
var maxBaseline by remember { mutableStateOf(0f) }
fun updateMaxBaseline(textLayoutResult: TextLayoutResult) {
maxBaseline = max(maxBaseline, textLayoutResult.size.height - textLayoutResult.lastBaseline)
}
val topBaselinePadding = with(LocalDensity.current) { maxBaseline.toDp() }
Text(
text = "One",
modifier = Modifier.paddingFromBaseline(bottom = topBaselinePadding),
fontSize = 20.sp,
onTextLayout = ::updateMaxBaseline
)
Image(
painter = painterResource(id = R.drawable.ic_launcher_background),
contentDescription = "",
modifier = Modifier
.padding(bottom = topBaselinePadding)
.size(24.dp)
)
Text(
text = "two",
modifier = Modifier.paddingFromBaseline(bottom = topBaselinePadding),
fontSize = 40.sp,
onTextLayout = ::updateMaxBaseline
)
}

How to achieve this layout in Jetpack Compose

I'm trying to use the new Jetpack Compose UI framework, but I'm running into an issue. I'd like to achieve this layout, which in xml is pretty easy to achieve:
But I can't figure out how to make the vertical divider take up the available vertical space, without specifying a fixed height. This code that I've tried doesn't seem to work:
#Composable
fun ListItem(item: PlateUI.Plate) {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
elevation = 2.dp
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Code")
Text(text = item.code)
}
Spacer(
modifier = Modifier
.preferredWidth(1.dp)
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
)
Spacer(modifier = Modifier.weight(1f))
Text(
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 34.dp),
text = item.name
)
Spacer(modifier = Modifier.weight(1f))
}
}
}
I keep getting this result:
I also tried with ConstraintLayout, but it still didn't work
#Composable
fun ListItem(item: PlateUI.Plate) {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
elevation = 2.dp
) {
ConstraintLayout(
modifier = Modifier.fillMaxWidth(),
) {
val(column, divider, text) = createRefs()
Column(
modifier = Modifier
.padding(8.dp)
.constrainAs(column){
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
},
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Code")
Text(text = item.code)
}
Spacer(
modifier = Modifier
.preferredWidth(1.dp)
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
.constrainAs(divider){
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(column.end)
}
)
Text(
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 34.dp)
.constrainAs(text){
start.linkTo(divider.end)
end.linkTo(parent.end)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
},
text = item.name
)
}
}
}
But nothing seems to work. Is this a bug, a missing feature or am I just missing something?
EDIT: Apparently the real problem is that the divider doesn't know how to measure when the Surface doesn't have a fixed height, setting height equal to some number solves the issue, but then the view doesn't adapt to the content height anymore, so this can't be the solution
You can apply:
the modifier .height(IntrinsicSize.Max) to the Row
the modifiers .width(1.dp).fillMaxHeight() to the Spacer
You can read more about the Intrinsic measurements here.
Something like:
Row(
modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
verticalAlignment = Alignment.CenterVertically
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
....
) {
Text(text = "....")
}
Spacer(
modifier = Modifier
.width(1.dp)
.fillMaxHeight()
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
)
Text(...)
}
You can set Intrinsic.Max for the preferredHeight of the Row, then set the Spacer to fill max height. You can read more on Intrinsics in this codelab section.
#Composable
fun ListItem() {
Surface(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp),
elevation = 2.dp
) {
Row(
modifier = Modifier.fillMaxWidth().preferredHeight(IntrinsicSize.Max),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Code")
Text(text = "2456")
}
Spacer(
modifier = Modifier
.preferredWidth(1.dp)
.fillMaxHeight()
.background(color = Color.Black.copy(0.12f))
)
Spacer(modifier = Modifier.weight(1f))
Text(
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 34.dp),
text = "Some name"
)
Spacer(modifier = Modifier.weight(1f))
}
}
}
I've solved it using constraint layout:
Box(modifier = Modifier.padding(Dp(50f))) {
ConstraintLayout(
modifier = Modifier
.border(width = Dp(1f), color = Color.Black)
.fillMaxWidth()
) {
val (left, divider, right) = createRefs()
Column(
modifier = Modifier
.padding(horizontal = Dp(20f))
.constrainAs(left) {
width = Dimension.wrapContent
start.linkTo(parent.start)
top.linkTo(parent.top)
end.linkTo(divider.start)
bottom.linkTo(parent.bottom)
}
) {
Text(text = "Code")
Text(text = "A12")
}
Box(
modifier = Modifier
.width(Dp(1f))
.background(Color.Black)
.constrainAs(divider) {
width = Dimension.wrapContent
height = Dimension.fillToConstraints
start.linkTo(left.end)
top.linkTo(parent.top)
end.linkTo(right.start)
bottom.linkTo(parent.bottom)
}
)
Box(
modifier = Modifier
.constrainAs(right) {
width = Dimension.fillToConstraints
start.linkTo(divider.end)
top.linkTo(parent.top)
end.linkTo(parent.end)
bottom.linkTo(parent.bottom)
}
) {
Text(
text = "Test",
modifier = Modifier
.padding(vertical = Dp(100f))
.align(Alignment.Center)
)
}
}
}
The key part is using that modifier height = Dimension.fillToConstraints
There are plenty of solutions here, but I thought I could demonstrate the ConstraintLayout approach and add a helpful usage of the IntrinsicSize enum that solves one of the issues (needing an adaptive height for the composable). Interestingly, either IntrinsicSize.Max or IntrinsicSize.Min will yield the desired behavior.
I used most of your code. The key differences are:
declares a guideline (my value passed in for the fraction does not produce the exact result you were looking for, but can be adjusted easily (use a fraction slightly smaller than .2) This can be useful if you expect wrapContent to alter your left Text to vary the location of a spacer, but would prefer a consistent spacer location across a list of these items.
others have mentioned, spacer modifier should include .fillMaxHeight()
define the height of the surface wrapper to be .height(IntrinsicSize.Min) docs ref here: https://developer.android.com/jetpack/compose/layout#intrinsic-measurements
divider start is constrained to the guideline
had to change the Spacer modifier to access the width, instead of preferredWidth
#Composable
fun ListItem(item: Plate) {
Surface(
modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Min),
shape = RoundedCornerShape(8.dp),
elevation = 2.dp
) {
ConstraintLayout(
modifier = Modifier.fillMaxWidth(),
) {
val guideline = createGuidelineFromStart(0.2f)
val(column, divider, text) = createRefs()
Column(
modifier = Modifier
.padding(8.dp)
.constrainAs(column){
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
end.linkTo(guideline)
},
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Code")
Text(text = item.code)
}
Spacer(
modifier = Modifier
.constrainAs(divider){
top.linkTo(column.top)
bottom.linkTo(column.bottom)
start.linkTo(guideline)
}
.width(1.dp)
.fillMaxHeight()
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
)
Text(
modifier = Modifier
.padding(horizontal = 8.dp, vertical = 34.dp)
.constrainAs(text){
start.linkTo(divider.end)
end.linkTo(parent.end)
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
},
text = item.name
)
}
}
}
I think Row layout is enough.
#Preview(showBackground = true, heightDp = 100)
#Composable
fun ListItem(item: PlateUI.Plate = PlateUI.Plate()) {
Card(
shape = RoundedCornerShape(8.dp)
) {
Row(
modifier = Modifier
.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier.padding(8.dp),
text = "Code\n${item.code}",
textAlign = TextAlign.Center
)
Box(
Modifier
.fillMaxHeight()
.width(1.dp)
.background(color = MaterialTheme.colors.onSurface.copy(0.12f))
)
Text(
modifier = Modifier
.weight(1f)
.padding(8.dp),
text = item.name,
textAlign = TextAlign.Center
)
}
}
}

Categories

Resources